JS函数杂谈

来源:互联网 发布:美服lol汉化补丁mac 编辑:程序博客网 时间:2024/05/16 05:35

记录下JS中函数的对象特质、方法特质、构造函数特质,以及一些觉得必要的细节。

知识前提:对prototype和__proto__有基本的认识。测试环境:chrome。

函数是对象是方法也是构造函数

var func = function(a) {    return a;}func.var_a = 1;console.log(func.var_a);//1console.log(func(2));//2
JS中一切皆对象,函数可以像普通对象一样被添加属性。同时,不影响其方法特质。
var func = function(a) {    return a;}console.log(typeof func(2));//numberconsole.log(typeof new func(2));//object
第一行输出体现的是函数的方法特质,第二行就是构造函数的特质了。前者类似调用了一个方法,后者是构造出了一个新的对象。

所以,函数它就在那里,看你如何用它

对象的成员访问

用函数构造了一个对象,然后怎么访问对象中的成员呢。
var func = function(a) {    this.inner_a = 1;    return a;}func.prototype.pro_a = 1;func.var_a = 1;var obj_a = new func();var obj_b = new func();console.log(obj_a.var_a);//undefinedobj_a.inner_a = 2;console.log(obj_a.inner_a);//2console.log(obj_b.inner_a);//1
var_a是func的成员,而obj_a和obj_b是根据func构造而来的对象,自然无法访问func的成员。inner_a是obj_a和obj_b的成员,并且两者的inner_a不是同一个,因此改变a的自然不影响b的。
obj_a.pro_a = 2;console.log(obj_a.pro_a);//2console.log(obj_b.pro_a);//1
obj_a和obj_b本身都没有pro_a,然后给obj_a添加pro_a,再执行输出。a输出的是自身的pro_a,b输出的是prototype的pro_a。
func.prototype.pro_a = 2;console.log(obj_a.pro_a);//2console.log(obj_b.pro_a);//2
修改obj_a和obj_b构造函数func的prototype中的成员后的输出。所以,js中对对象的成员的遍历可见一斑。这应该是所谓原型链的范畴吧。

详解构造函数

var func = function(a) {    return a;}var func_b = function() {}console.log(new func().__proto__ == func.prototype);//true
上述代码表明了,实例的__proto__确实是指向构造函数的prototype。JS遍历对象成员的时候依据的应该就是对__proto__的逐级遍历。
var func = function(a) {    return new func_b();}var func_b = function() {}console.log(new func().__proto__ == func.prototype);//falseconsole.log(func().__proto__ == func.prototype);//falseconsole.log(new func().__proto__ == func_b.prototype);//trueconsole.log(func().__proto__ == func_b.prototype);//true
如果在func中返回func_b的实例。调用func()很好理解,会生成一个func_b的实例,console也证明这一点。如果生成func的实例,则生成后的实例其实就是func_b的实例。也就是说,此处func()和new func()效果一样。jQuery的构造函数就是这样的。

来点折腾-1

var func = function(a) {    return func_b();}var func_b = function() {}console.log(new func().__proto__== func.prototype);//trueconsole.log(func().__proto__ == func.prototype);//抛异常console.log(new func().__proto__ == func_b.prototype);console.log(func().__proto__ == func_b.prototype);
如果在func中返回func_b的调用。由于func_b()不返回任何东西,那new func()生成的就是func的实例,自然第一条输出是true的。调用func()就和调用func_b效果一样,不返回任何东西,没有__proto__属性,自然抛异常。

来点折腾-2

var func = function(a) {    return func_b;}var func_b = function() {}console.log(new func().__proto__== func.prototype);//falseconsole.log(func().__proto__ == func.prototype);//falseconsole.log(new func().__proto__ == func_b.prototype);//falseconsole.log(func().__proto__ == func_b.prototype);//falseconsole.log(new func() == func());//trueconsole.log(new (new func())().__proto__== func_b.prototype);//trueconsole.log(new (func())().__proto__== func_b.prototype);//true
如果在func中返回func_b。那么,func()和new func()其实返回的都是对象func_b。因此,new (func())()和new (new func())()相当于new func_b()。最后两行console输出也证明了这点。

小结

1,函数具备对象特质。
关于对象成员遍历会涉及原型链(proto在其中扮演关键角色),简单点说就是向上逐级遍历,类似java之类的语言中,子类的成员如果不存在会向父类遍历。
2,函数同时具备方法特质和构造函数特质。
当构造函数中返回了另外一个构造函数,或者返回一个新建的对象,则构造函数返回的不再是构造函数本身构造的实例。此处用instanceof运算符能更加直白的证明。因此,函数构造是有嵌套概念在的,jQuery的构造就利用了此点。

模拟下jQuery的构造

var func = (function(){    var func = function() {        return new func_a();    }    func.prototype.a = 2;    var func_a = function() {    }    func_a.prototype = func.prototype;    return func;})();console.log(func().a);//2
自调用匿名函数其实返回的是func_a的实例。为了能访问func的prototype中的内容,重置了func_a的prototype。
原创粉丝点击