- JS代码还没有运行的时候,JS环境里面已经有一个浏览器提供的window对象了。
- 除此之外,还有console、document、Object、Array和Function等,且都被挂到了window上。
我们写一段代码:
var obj={}
obj.toString()
这两句代码干了什么?为什么不报错?为什么obj有toString()属性?
当我们写 var obj={} 的时候,就在内存中创建了一个空对象,这个空对象里面有个隐藏属性__proto__指向window.Object.prototype。
当去调用obj.toString()的时候,obj本身没有toString,然后就去obj.__proto__上面去找。
所以当我们调用obj.toString()的时候,实际上调用的是window.Object.prototype.toString()。
我们再看一段代码:
var arr = []
arr.push(1)
这两句代码干了上什么?
同上面类似,var arr=[]会让arr指向一个空对象,然后arr.__proto__指向window.Array.prototype。
当调用arr.push的时候,arr自身没有这个属性,就会去__proto__上面找push。
因此arr.push实际上就等于window.Array.prototype.push。
那如果我们调用arr.valueOf()呢?
首先是arr自身没有这个属性,然后就去arr.__proto__上面去找,结果还是没有。
因为Array也是函数,window.Array.prototype上面也有__proto__属性,所以这时就要去arr.__proto__.__proto__上面去找了。
所以arr.valueOf其实就是window.Object.prototype.valueOf。
那__proto__和prototype有什么区别呢?
prototype 指向一块内存,这个内存里面有共用属性。
__proto__ 指向同一块内存。
他们都存着原型的地址。
他们的不同点是 prototype 是构造函数的属性,而 __proto__ 是对象的属性。但是注意构造函数也是对象哟。
问什么是原型链?
假设我们有一个数组对象 x=[],x有一个隐藏属性__proto__,这个属性会指向window.Array.prototype。
即x.__proto__ === window.Array.prototype。
这时,我们就说x的原型是window.Array.prototype。
而__proto__的唯一作用就是用来指向原型的。
那什么是原型链呢?
window.Array.prototype也有一个__proto__属性指向window.Object.prototype。
比如x.valuOf === window.Object.prototype.valueOf。
这时,x就有两个原型:
- x的原型是window.Array.prototype。
- x的原型的原型是window.Object.prototype。
于是就通过隐藏属性__proto__形成一个链条。这个链条就是原型链。
怎么创建新的原型链呢?
假设有两个对象a={},b={}
那我们直接a.__proto__ = b, 这样是不是就形成了一个新的原型链?答案是否,这种写法JS不推荐。
两种正确写法:
a = Object.create(b), 即把a的原型置于b。a = new cb(), 这种写法cb必须是构造函数,会导致x.__proto__ === cb.prototype。
解决了什么问题?
在没有 Class 的情况下实现「继承」。以 a ===> Array.prototype ===> Object.prototype 为例,我们说:
- a 是 Array 的实例,a 拥有 Array.prototype 里的属性。
- Array 继承了 Object。
- a 是 Object 的间接实例,a 拥有 Object.prototype 里的属性。
优点
简单、优雅。
缺点
跟 class 相比,不支持私有属性。
怎么解决缺点
使用 class 。但 class 是 ES6 引入的,不被旧 IE 浏览器支持。