发表于: 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;



文/pinggod(简书作者)
原文链接:http://www.jianshu.com/p/5984b51b3d99
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。




返回列表 返回列表
评论

    分享到