学习JavaScript"原型"

在上一节屡次提到[[Prototype]]链,但没解释它究竟是什么,那么这一节咱们来详细介绍一下。学习

[[prototype]]

认识[[prototype]]

JavaScript中的每一个对象都拥有一个原型对象。其实就是对于其余对象的引用。几乎全部的对象在建立时[[Prototype]]属性都会被赋予一个非空的值。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链。(mdn有详细的说明)ui

为了学习体验,咱们打开控制台而后输入如下代码:this

var object = {
    name:"tom"
};

object.__proto__
复制代码

能够看到控制台打印了一堆属性和方法,这个咱们后续说明。spa

思考下面的代码:prototype

var object = {
    name:"tom"
}

var newObject = Object.create(object);

newObject.name; // "tom"
复制代码

这里[[Prototype]]引用就发挥了做用,当咱们试图访问newObject对象的name属性时会触发[[Get]]操做,[[Get]]操做的第一步是检查对象自己是否有这个属性,若是有的话就使用它。但若是name不在newObject中,就要使用对象的[[Prototype]]链了。设计

如今newObject对象的[[Prototype]]关联到了object。显然newObject.name并不存在,可是它在 object中找到了name属性。可是若是object中也找不到name而且[[Prototype]]链不为空,就会继续查找下去。3d

这个过程会持续到找到匹配的属性名或者查找完整条[[Prototype]]链。若是是后者的话,[[Get]]操做的返回值是undefined。(for..in遍历对象时原理和查找[[Prototype]]链相似)code

[[prototype]]的尽头

从图中,能够看出来[[Prototype]]链最终都会指向内置的Object.prototype,这上面有许多咱们熟悉的功能,好比toString()valueOf()hasOwnProperty(..).isPrototypeOf(..)等等。cdn

对象属性设置

给一个对象设置属性并不单单是添加一个新属性或者修改已有的属性值。对象

如今咱们来学习一下这个过程:

var object = {};

object.name = "tom";
复制代码
  • 若是object对象中包含名为name的数据访问属性,那么这条赋值语句只会修改已有的属性值。
  • 若是name不在object中,[[Prototype]]链就会被遍历,相似[[Get]]操做。若是原型链上找不到namename就会被直接添加到object上。
  • 若是属性名name既出如今obejct中也出如今obejct[[Prototype]]链上,那么就会发生屏蔽。obejct中包含的name属性会屏蔽原型链上的全部name属性,所以object.name老是会选择原型链中最底层的name属性。
  • 最后一种状况略微复杂咱们单独说明一下,若是name仅存在于原型链上层,那么赋值语句object.name = "tom"的行为就会有些不一样。

下面分析一下若是name是存在于原型链上时object.name = "tom"会出现的三种状况。

  1. 若是在[[Prototype]]链上存在名为name的数据访问属性而且没有被标记为只读(writable:false),那就会直接在obejct中添加一个名为name的新属性,它是屏蔽属性。
var object = {};

var other = Object.create(object);

other.name = "tom"; //默认other的defineProperty的writable属性为true

console.log(object.name); //tom
复制代码
  1. 若是在[[Prototype]]链上存在name,可是它被标记为只读(writable:false),那么没法修改已有属性或者在obejct上建立屏蔽属性。
var object = {};

var other = Object.create(object);

other.name = "tom"; 

Object.defineProperty(other,"name",{
    writable:false
});

console.log(object.name); //undefined
复制代码
  1. 若是在[[Prototype]]链上存在name而且他是一个Setter,那就必定会调用这个Settername不会被添加到object上,也不会从新定义name这个Setter
var object = {};

var other = Object.create(object);

Object.defineProperty(other,"name",{
    get:function(){
      return this._name;  
    },
    set:function(){
        this._name = "tom";
    }
});

console.log(object.name); //undefined
复制代码
  • 注意属性产生的隐式屏蔽。代码以下:
var object = {
    num: 6
};

var newObject = Object.create(object);

console.log(newObject.num);  //6
console.log(object.num);  //6

console.log(newObject.hasOwnProperty("num"));  //false
console.log(object.hasOwnProperty("num"));  //true

newObject.num++;

console.log(newObject.num);  //7
console.log(object.num);  //6
复制代码

这里其实也比较好说明,执行++操做至关于newObject.num=newObject.num+1。所以++运算首先会经过[[Get]]操做在[[Prototype]]查找属性num并从object.name获取当前属性值,而后给这个值加1,接着用[[Set]]将值7赋给newObject中新建的屏蔽属性name

修改委托属性时必定要当心。惟一的办法是object.num++

小结

到此简单的介绍了一下原型的基本概念。下一节将介绍原型更多的特性。

参考

相关文章
相关标签/搜索