Javascript之对象和原型链

时间:2022-08-24 22:39:11

  几乎任何对象有一个[[prototype]]属性,在标准中,这是一个隐藏属性。该属性指向的是这个对象的原型。
  几乎所有对象的原型链末端都是Object.prototype

1. 字面量方式

    var obj1={x:1,y:2}
var obj2={
x:1,
y:2,
o:{
z:3,
n:4
}
}

  如上,直接在大括号中包裹对象的属性key及其值value。


2. 构造函数模式

function sum(a,b){
this.aa= a;
this.bb= b;
this.add = function(){
return (aa+bb);
}
}
var calc =new sum(2,3);
console.log(calc.add) //5

   注意:这个new关键字是必须,如果不加,sum就不会当成构造函数调用,而只是一个普通的函数,这时候,sum()内部的this指向就会是全局window或者global,将会意外地给他的外部作用域即window添加属性。


3. Object.create()

var obj=Object.create({x:1})
obj.x //1
typeof obj.toString //function
obj.hasOwnProperty('x') //false

var obj=Object.create(null)
obj.toString //undefined

  第一行代码,create()方法是Object内置方法,接收了一个对象作为参数,使用访问obj.x将会返回值1

  但是需要注意的是,使用这种方式创建的对象,其实是将obj__proto__指向了{x:1}这个对象,并不是在obj本身内部创建了一个置为1的属性x,所以hasOwnProperty()方法的时候,将会返回false

  正是因为Object.create()能够重置对象的__proto__指向,所以如果在第5行代码中,将obj的原型指向了null之后,obj的原型链末端就从Object.prototype变成nullobj本身以及原型链上都不在包含toString这个方法,所以将会返回undefined


4. 原型链方式

    function foo(){}
foo.prototype.z=3
var obj=new foo()
obj.y=2
obj.x=1

obj.x //1
obj.y //2
obj.z //3
typeof obj.toString //function
'z' in obj //true
obj.hasOwnProperty('z') //false

  以上代码,首先使用function 定义了一个函数对象,这个函数对象默认带有了一个名为prototype 的隐藏属性,这个prototype 属性是一个对象类型的属性,指向这个对象的原型,并且在这个属性中存在一个名为__proto__的标签。
  在第二行代码中,又为这个prototype 属性增加了一个属性,所以现在的prototype 如下:

      Javascript之对象和原型链

  第三行代码中,使用new关键字实例化了一个obj,这个obj 对象的原型__proto__将会指向构造函数的prototype 属性,也就是foo.prototype,然后在这个obj 对象上,增加了两个属性。

  以上几行代码可用下图表示:

      Javascript之对象和原型链

  当访问obj.x 的时候,obj本身就已经存在x 这个属性,所以直接返回对应的值1,同理访问obj.y 返回对应值2
  当访问obj.z 的时候,发现obj本身并不存在z 这个属性,所以沿着原型链往上找,也就是内部__proto__ 标签指向的构造函数的prototype 属性foo.prototype,在这上面找到了z,并返回对应值3

  实际上,这个__proto__ 可以继续指向下去,这就构成了原型链,实际上,如果在上一步没有找到z 属性的话,那么还会继续沿着foo.prototype__proto__往下继续找,直到找到这个z 属性,或者到达原型链的末端,查找才算结束,返回找到的对应值或者undefined

  而这条原型链的最末端就是Object.prototype,它里面的__proto__指向null

  正是因为原型链的存在,所以一些类似于toString(注意,这里没有括号,不是toString() )的方法,尽管我们并没有在obj上定义,但是因为这个toString方法是在Object.prototype上存在的,也就是处在 obj的原型链上,所以obj也能拿到这个方法。

  因为obj.z属性是从原型上继承而来,所以当使用in方法的时候,会返回true,但是如果使用hasOwnProperty()的方法,将会返回false,因为这个z属性并不是obj本身(own)的属性,而是继承于原型链。


    obj.z=5
obj.hasOwnProperty('z') //true
foo.prototype.z //3,不变
obj.z //5,发生了变化

obj.z=undefined
obj.z //undefined

delete obj.z
obj.z //3

  上面说到,obj.z返回的结果,其实是从obj的原型链上找到的,并不是obj这个对象本身的属性,那么如果在obj对象上再增加一个obj本身的z属性,将会发生什么呢?
  第二段代码的第一行,在obj对象上增加了一个z 属性,值为5,然后使用hasOwnProperty()方法,返回结果true,这表明现在返回的z属性是obj 对象本身的属性,并不是原型链上返回的。
  而这个时候,其实obj原型链上的z属性并没有修改,也没有删除,还是保持原样,所以foo.prototype.z 的值仍然是3
  原因还是在于对象属性的遍寻机制上。
  在obj.z=5这一句,首先判断obj这个对象是否存在z这个属性,如果存在就将这个z属性的值修改为5,没有就直接创建,并不会在原型链上查找,所以obj这个对象内部,就被创建出了z这个属性。
  而在obj.z 进行访问的时候,首先先在这个对象obj本身寻找z这个属性,如果没找到再沿原型链往上找,但是现在已经在它本身找到了,所以就不再往上找了,返回值为5

      Javascript之对象和原型链

  这里需要注意的是,如果还想访问原型链上的z 属性,那么将obj.z置为undefined或者null,都是没用的,最终访问obj.z的时候,将会返回undefined或者null,而不是5.
  这是因为将obj.z置为undefined或者null,只是将obj.z这个属性对象的值,我们可以称之为value置为了undefined或者null,而obj这个对象本身的z属性,我们可以称之为key,其实还是存在的,所以访问的依旧是obj本身的z属性,而不是原型链上的。
      Javascript之对象和原型链

  这个时候,可以使用delete关键字,将obj上的z属性完全删除。
  注意,这个delete关键字,只会删除obj本身的z属性,并不会删除obj原型链上的z属性。
  删除之后,obj对象中就不存在z这个属性(key)了,然后再访问z这个属性的时候,就会在原型链上查找。