发表于: 2016-10-14 00:10:11
1 1827
今天完成的事情:看了一波原型改写
明天计划的事情:oop
收获:
在 JavaScript 中,原型(prototype)是非常重要的概念。JavaScript 的对象模型就是完全基于原型的,并由此构成了原形链,实现面向对象中的继承等特性。
在我们创建函数时,默认都会包含 prototype 属性,指向一个空的原型对象。
function func(){}console.log(func.prototype);//func{}
这个空对象可以被不断重写,所有基于这个原型的对象都会继承改写后的方法和属性。
添加/使用原型的方法和属性
function Func(){}
Func.prototype.name = "Sean";
Func.prototype.getInfo = function() { return this.name;
}var person = new Func();console.log(person.getInfo());//"Sean"console.log(Func.prototype);// Func { name="Sean", getInfo=function()}
在 JavaScript 中,对象是通过引用来传递的,因此我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。
自身属性与圆形属性
当 JavaScript 引擎检索对象属性时,它会遍历对象的所有属性,如果找到,就返回属性值;如果找不到,就会查找创建当前对象的构造器函数的原型(thisObj.constructor.prototype)。如此递推下去,如果没有找到,就一直检索到 Object 内建对象。
属性默认值
给属性设置默认值,在 JavaScript 并没有官方的方式,但我们可以利用自身属性重写原型属性这一特性来实现。
function Func(){ this.name = "Sun";
}
Func.prototype.name = "Sean";
Func.prototype.getInfo = function() { return this.name;
}var person = new Func();console.log(person.getInfo());//"Sun"delete person.name;console.log(person.getInfo());//"Sean"
枚举属性
如果想要遍历某个对象的所有属性(实际上并非所有属性),我们可以使用 for-in 循环。
function Func(){ this.name = "Sun";
}
Func.prototype.name = "Sean";
Func.prototype.getInfo = function() { return this.name;
}var person = new Func();for(var i in person) { console.log("i : " + i)
}
这里有些细节需要注意:
- for-in 循环不会显示所有的属性,比如(数组的)
length
属性和constructor
属性。那些已经显示的属性称为可枚举属性,可以通过propertyIsEnumerable()
方法来判断。 - 原形链中的各个原型属性也会显示出来,前提是可枚举的。可以通过
hasOwnProperty()
方法来判断一个属性是对象自身属性还是原型属性。 - 对于所有的圆形属性,propertyIsEnumerable() 都会返回 false,包括那些 for-in 循环中可枚举的属性。
神秘的 __proto__
person.constructor.prototype === person.__proto__//true
谨慎用于上线环境,因为该属性在 IE 中不存在,所以不具有跨平台能力。
扩展内建对象
既然每个对象都有原型,那么内建对象也必然有一个原型对象。我们给内建对象的原型对象添加属性和方法,相应的就会在与之相关的对象身上生效。
String.prototype.reverse = function() {
retrun Array.prototype.reverse.apply(this.split("")).join("");
}
由于 JavaScript 开发的逐步深入,某些曾经不存在的方法,也许未来就实现了。所以有必要在扩建方法前进行能力检测。
if(!String.prototype.reverse){}
陷阱
在处理原型问题时,特别需要注意以下两种行为:
- 当我们对原型对象执行完全替换时,可能会触发原形链中的某种异常
- prototype.constructor 不可靠
function Dog() { this.name = "Nick";
}var nick = new Dog();
Dog.prototype.getInfo = function() { return this.name;
};
nick.getInfo();//"Nick"nick.constructor;//Dog()
以上都很正常,但如果此时查看一下原型对象的构造器,就会发现不对的地方了。
console.log(nick.constructor.prototype.constructor);//Dog(),本应该是 Object()console.log(typeof nick.constructor.prototype.name);//undefined,本应该是存在的
现在,重新定义一个新的原型对象。
Dog.prototype = { this.age = 10; return this.age;
}
nick.age//undefinedtypeof nick.__proto__.getInfo//function
这说明,原有对象不能访问新原型对象的属性,但它依然通过神秘链接 __proto__
与所有的原型对象保持联系。
而我们之后创建的对象使用的都是更新后的原型对象对象,而且新创建对象的 __proto__
链接也指向了更新后的原型对象。
但这个时候,新对象的 constructor
属性就不能保持正确了。
我们可以通过下述方式解决上述的所有异常行为:
Dog.prototype = { //...
}
Dog.prototype.constructor = Dog;
原文链接:http://www.jianshu.com/p/5984b51b3d99
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
评论