原型
1. [[Prototype]]
JS中的对象有一个特殊的[[Prototype]]内置属性,实际上就是对于其他对象的引用,
var myObject = {
a:2
}
console.log(myObject.a) //打印2
对于默认的[[Get]]操作来说,如果无法再对象本身找到需要的属性,就会继续访问对象的[[Prototype]]链。
var anotherObject = {
a:2
}
var myObject = Object.create(anotherObject)
console.log(myObject.a)
Object.create
创建一个对象并把这个对象的[[Prototype]]关联到指定的对象。
在myObject中找不到a,就会沿着[[Prototype]]链找下去,直到找到匹配的属性名或找完整条链。
所有普通的[[Prototype]]链最终都会指向内置的Object.prototype
。
2.“类”
2.1 “构造”
function foo(){
}
console.log(foo.prototype) // {}
这个就是原型,每当通过new foo()创建的每个对象最终都会被[[Prototype]]链接到这个对象。
function foo(){
}
var a = new foo()
console.log(Object.getPrototypeOf(a) === foo.prototype) //true
这样两个对象就通过[[prototype]]关联起来了,没有用到我们常见的类。
new
会劫持所有普通函数并用构造对象的形式来调用它。
function foo(){
console.log('????????????')
}
var a = new foo() //??????????
console.log(a) //{}
foo只是一个普通的函数,但当使用new调用时,就会构造一个对象并赋值给a,所以new无论如何都会构造一个对象,这是一个构造函数调用,但foo本身并不是构造函数。
因此在js中,所谓的“构造函数”,实际上指的是带new的函数调用将两个对象关联起来。
看一下这段代码:
function foo(name){
this.name = name
}
foo.prototype.myname = function(){
return this.name
}
var a = new foo('a')
var b = new foo('b')
console.log(a.myname())
console.log(b.myname())
在这里,创建a和b的时候并不是将foo.prototype
对象复制到这两个对象中,而是将其内部的[[Prototype]]关联到foo.prototype
上,当a和b中无法找到myname时候,就会通过委托在foo.prototype
上找到。
因此再看这段代码:
function foo(){
}
foo.prototype.constructor === foo //true
var a = new foo()
a.constructor === foo //true
实际上就是其a.constructor
引用被委托给foo.prototype
,在a上找不到的时候就会去到它所关联的foo.prototype上找,而由于foo.prototype.constructor
是一个默认属性,默认指向foo的,所以最后一个判断为真,so这里并不是真正的“构造函数”(不要被constructor骗了)。
但如果我们手动foo.prototype
创建要给新的对象会发生什么?
stedafunction foo(){
}
foo.prototype = {
}
var a = new foo()
console.log(a.constructor === foo) //false
console.log(a.constructor === Object) //true
由于我们给foo.prototype
指定了一个新的对象,它已经没有.constructor
属性了(默认自带的才有),而由于a会沿着原型链来查找,到了foo.prototype仍然没找到,那就只能再往上一层(Object.prototype)里面找了,这里有.constructor,而且是指向Object它自己。
因此这里进一步说明了a并不是简单地由new foo()通过constructor构造而来,而是通过js独特的机制原型链而进行两个对象之间的关联。
2.2 “继承”
在js中,”继承“是把一个对象的prototype与另一个对象的prototype关联起来,有两种方法:
-
Bar.prototype = Object.create(foo.prototype) //ES6之前的方法
-
Object.setPrototypeOf(Bar.prototype,foo.prototype) //ES6的方法
2.3 “反射”
在java中,检查一个实例的继承祖先被称为反射,而在JS中,查找对象的委托关联也是类似"反射"这样的机制:
function Foo(){
}
Foo.prototype.blah = {
}
var a = new Foo()
a instanceof Foo //在a的整条原型链中是否有Foo.prototype指向的对象
Foo.prototype.isPrototypeOf(a) //在a的整条原型链中是否出现过Foo.prototype
第一种方法只能处理对象和函数之间的关系,无法判断两个对象之间是否通过原型链关联。
在ES5和ES6标准下,都有方法可以直接访问内部的原型属性:
Object.getPrototypeOf(a) === Foo.prototype //ES5
a.__proto__ === Foo.prototype //ES6
联。
在ES5和ES6标准下,都有方法可以直接访问内部的原型属性:
Object.getPrototypeOf(a) === Foo.prototype //ES5
a.__proto__ === Foo.prototype //ES6