JavsScript二三事(1)

Section A

标签:代码优化 闭包

下面有三个给元素添加事件的浏览器兼容版本,这三个版本代表着不同的效率。

版本1:
var addEvent = function(ele, type, listener, useCapture) {
    if(ele.addEventListener) {
        return ele.addEventListener(type, listener, useCapture);
    } else {
        return ele.addEvent('on' + type, listener);
    }
}

版本2:
var addEvent = (function() {
    if(document.addEventListener) {
        return function(ele, type, listener, useCapture) {
            ele.addEventListener(type, listener, useCapture);
        }
    } else {
        return function(ele, type, listener, useCapture) {
            ele.attachEvent('on' + type, listener);
        }
    }
})();

版本3:
var addEvent = document.addEventListener ? 
    function(ele, type, listener, useCapture) {
        ele.addEventListener(type, listener, useCapture);
    } :
    function() {}unction(ele, type, listener, useCapture) {
        ele.attachEvent('on' + type, listener);
    };

可以看出第一个版本简单清晰,也是是最常见的写法:当给元素添加事件时判断浏览器是否支持addEventListener如果不支持使用addEvent。但是这样做的话,每次给元素添加事件都要做一次兼容性判断,而这显然是没必要的。所幸,函数的功能无比强大,让我们看看版本二。版本二中,当外层函数执行到给addEvent赋值时,匿名函数会对addEventListener兼容性做出判断,并返回一个相应的函数。

在支持addEventListener的环境下:
addEvent = function(ele, type, listener, useCapture) {
    ele.addEventListener(type, listener, useCapture);
}
不支持的环境下:
addEvent = function(ele, type, listener, useCapture) {
    ele.attachEvent('on' + type, listener);
};

这样就不需要每次调用addEvent都做判断了。但是这样写看起来不美观,也难以理解。Javascript给我们提供个三元操作符A ? a : b; 它会将A转换为Bool值进行判断,如果为真则该表达式变为a,为假则表达式变为b。于是有了版本三,我们用该三元操作符替换了外层包裹的匿名自执行函数,它返回与版本二同样的值,简单明了。

Section B

标签:常见错误 函数命名 无限递归

下面是获取元素实际样式getComputedStyle的兼容性版本实现。下面是版本一。

版本1:
window.getComputedStyle = function(element) {
        if(window.getComputedStyle) {
            return window.getComputedStyle;
        } else {
            return element.currentStyle;
        }
    }

版本一是很多人的都会写成的版本。但是这个版本却是错误的,在调用该函数后,会出现溢出。为什么呢?上面逻辑很正确啊:首先判断浏览器是否支持getComputedStyle,然后返回浏览器自己支持的属性或函数。先来看下面这个例子。

function haha() {
    if(window.haha) alert(1);
}
haha();

在浏览器执行,浏览器会弹出一个1,能看出什么吗?其实当我们声明了haha函数,浏览器就已经存在window.haha了,所以执行到haha()里面的if(window.haha),结果必为真,于是乎这个if就显得没有任何意义了。上面的getComputedStyle也一样,由于我们使用了window对象自带的getComputedStyle作为自定义函数名,我们自定义的getComputedStyle函数是替代了原有函数的,于是执行我们自定义的函数返回的结果一定是window.getComputedStyle。于是,我们造就了个无限循环函数(getComputedStyle(elment)->getComputedStyle(element)->.......),导致溢出。类似下面图片。

1.png

解决的方法就是给自定义函数命个与浏览器自带函数不一样的名称,类似下面这种。

版本2:
window.getElementStyle = function(element) {
        if(window.getComputedStyle) {
            return window.getComputedStyle;
        } else {
            return element.currentStyle;
        }
    }

所以有一点要注意,避免让自定义的轮子与原本的轮子重名,除非你要改写原本的轮子,并保证不使用原有轮子。

Section C

标签:匿名函数 this

下面是个匿名函数有关的有趣的点:

(function haha() {
    if(window.haha) alert(1);
})();

还记得上面有个类似的函数吗,不同的是,这里我用括号把它包裹起来了,于是它...并没有弹出1,也就意味着被包裹起来的haha并不是window对象直接的一员(插一下javascript执行顺序,javascript是使用静态作用域的,函数在执行前会先将变量初始化)。事实上haha并不是window对象直接的一员,而是匿名函数的一员。当要执行该匿名函数时,会首先初始化该匿名函数,在内部声明叫hahad的函数,匿名函数则指向window对象。而又一个事实是,匿名函数的外部环境一定是window对象的,如何证明这点呢?看下面的例子。

var obj = {
    foo: function() {
        console.log(this);
        (function haha() {
            console.log(this);
            if(window.haha) alert(1);
        })();
    }
}
obj.foo();

执行结果是:

2.png

也就是说,即使匿名函数被定义在一个非window对象里面,它指向的也是window。不过虽说函数指向的是window但是它还是能够访问外层函数的属性的,这就牵扯出了变量作用域。千万别把this、变量作用域、原型链弄混。来看看下面的演示:

3.png

Section D

标签:this 变量作用域 原型链

this是在函数内部使用的一个指针,指向调用函数的对象。Javascript变量作用域是在程序源码中定义该变量的区域。很显然this指向的对象对应了某个作用域。
变量作用域在源码中就是被定义了的,并且它们一起构成强大的作用域链。于是乎一个函数天生就能访问外部函数的变量,我称这种能力为“先天异能”。这种异能甚是强大,如果把属性必做财产,方法比做打手,那么子函数就拥有使唤父辈财产和打手的权利。不过美中却有不足,比如,子函数对同名变量的访问是有顺序的,也就是说如果函数A的上层函数B中定义了var a = 10;A自己也定义了var a = 6; 那么A函数conosle.log(a)打印出的会是6而不是10。那么如果函数A想操作的是爸爸B的财产a该怎么办呢?this能够很好的解决这个问题。
相比于作用域链,我称this为“后天异能”,它有寻根溯源的能力。我们能够通过this访问this指向对象的属性与方法,因此this必须是可变的。不过this只有一个,而且它是函数内部自动生成的,要怎样使用这么珍贵的技能呢?.....(未完)

写累了。。。待续。。。。。
(续:继续BB this,扯一扯原型链,或许还能扯出其他什么东西,我总是这样。下面是为下篇准备的代码,牵扯到自定义浏览器属性访问 函数绑定 高阶函数 函数柯里化)

var Duck = {
    type: 'duck',
    speak: function() {
        alert('我是' + this.type);
    }
}
function createObj(pro) {
    if(Object.create) return Object.create(pro);
    var F = new Function();
    F.prototype = pro;
    return new F;
}

var duck = createObj(Duck);
duck.speak();

HTMLElement.prototype.__defineGetter__('children', function() {
    return Array.prototype.filter.call(this.childNodes, function(node) {
        return node.nodeType === 1;
    });
});
错误版本:
HTMLElement.prototype.__defineGetter__('children', function() {
    if(this.children) return this.children;
    else return Array.prototype.filter.apply(this.childNodes, function(node) {
        return node.nodeType === 1;
    });
});

window.__defineGetter__('foo', function() {
    alert(1);
});
window.foo;

function foo() {
    alert(1);
}
foo.apply(null);

function foo() {
    alert(1);
}
var a = foo.bind(null);
a();


Object.prototype.bind = function() {
    var _this = this,
        _target = arguments[0],
        _args = [].prototype.slice.call(arguments, 1);
    return function() {
        this.apply(_target, _args);
    }
}

function reduce(fn, base, array) {
    array.forEach(function(ele) {
        base = fn(ele, base);
    });
    return base;
}
function forEach(fn, array) {
    for(var i = 0; i < array.length; i++) {
        fn(array[i]);
    }
}
function sum(array) {
    function add(a, b) {return a + b;}; 
    return reduce(add, 0, array);
}
function add(m) {
    var total = m;
    var helper = function(n) {
        if(n == undefined) {return total}
        else {
            total += n;
            return helper;
        }
    }
    helper.valueOf = function() {
        return total;
    }
    return helper;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容