instanceof
instanceof 运算符用来检测 constructor.prototype(构造函数的原型) 是否存在于参数 object 的原型链上。它的实质就是通过 __proto__属性不断地在原型链上查找,找到返回true
obj instanceof constructor
<!--检测obj.__proto__==constructor.prototype-->
实现instanceof
function myInstanceof(instance, obj) {
let proto = instance.__proto__
let prototype = obj.prototype
while (true) {
if (proto === null) return false
if (proto === prototype) return true
proto = proto.__proto__
}
}
// test
function Foo(name){
this.name=name
}
let ins=new Foo('avicii')
console.log(myInstanceof(ins,Foo)); //true
Object.create(proto[,propertiesObject])
obj=Object.create(proto[,propertiesObject]),中 产生的新对象(obj)指向一个空的proto对象,不继承proto上面的属性和方法,但是因为这个方法使obj.__proto__=proto,所以obj能够通过原型链(__proto__)访问proto上面的方法和属性.proto最好为一个普通对象或者函数的原型对象,这样符合原型链规则.
Object.create(proto[, propertiesObject])我认为分两种使用情况, 一种是参数proto是一个对象,另一种是参数proto是一个函数的原型对象
Object.create(obj[, propertiesObject])
创建的新对象(ins)指向一个空的me对象,因为没有做相应的操作使ins继承obj里面的属性和方法,只是将ins.__proto__=me,ins能访问原型链上面各原型对象里面定义的方法和属性.
let me = {
name: "avicii"
}
let ins2 = Object.create(me)
console.log(ins2.__proto__ == me); //true
console.log(ins2, me, ins2.name); //{} { name: 'avicii' } avicii
console.log(ins2.name==me.name,ins2.prototype==me.prototype,ins2.__proto__.prototype==me.prototype,ins2.prototype==ins2.__proto__.prototype); //true true true true
// ins2指向一个空的me对象,因为ins2没有通过相应操作继承me里面定义的属性和方法,只是ins2.__proto__==me
// ins2可以通过原型链访问这个对象的属性和方法proto__
//因此ins2.prototype==me.prototype实质上是因为ins2本身还没有定义prototype,所以它顺着原型链(__proto__)找到me.prototype的内存地址引用,
// ins2.prototype,ins2.__proto__,me.prototype三者的内存地址是同一个
// 同理ins.name和me.name
Object.create(fn.ptorotype[, propertiesObject])
创建的新对象(ins)指向一个空的fn.prototype对象,不继承在fn.prototype上面定义的方法和属性,但是因为ins.__proto__=fn.prototype,因此ins能访问fn.prototype上面定义的方法和属性,而fn里面定义的属性和方法需要通过new fn()等操作使ins继承fn里面定义的属性和方法
function Say() {
this.name = 'avicii'
}
function newFunc() {
this.name = 'mxalive'
}
Say.prototype.age = 28
let ins1 = Object.create(Say.prototype)
newFunc.prototype = Object.create(Say.prototype)
newFunc.prototype.face = 'cool'
let ins2 = Object.create(newFunc.prototype)
console.log(ins1.__proto__ == Say.prototype); //true
console.log(ins1, Say.prototype, ins1.age);// Say {} Say { age: 28 } 28
// ins1指向一个空的Say.prototype对象,因为没有通过new 操作使ins1继承Say函数里面定义的属性和方法,
// 只是ins1.__proto__==Say.prototype
// ins可以通过原型链访问这个对象的属性和方法
console.log(ins2, newFunc.prototype, ins2.age, ins2.face);// Say {} Say { face: 'cool' } 28 cool
//ins2指向一个空的newFunc.prototype对象,而newFunc.prototype因为newFunc.prototype = Object.create(Say.prototype)从而指向了一个空的Say.prototype对象
// 因此ins2最后还是指向了一个空的Say.prototype对象,打印出Say {},ins2可以通过原型链(__proto__)访问newFunc.prototype和Say.prototype上面定义的属性和方法
////////////////////////////////////////////
总结
Object.create(proto[, propertiesObject]),proto为函数的原型对象符合原型链的原则,所以尽量不要使proto为函数,要么为普通对象,要么为函数的原型对象,根据相应的需求去处理
__proto__和prototype
只有函数拥有prototype,而对象不拥有
对象拥有__proto__
在Javascript里面函数也是一个对象,所以函数既拥有__proto__也拥有prototype
每个实例对象(object )都有一个隐式原型属性(称之为__proto__ )指向了创建该对象的构造函数的原型。也就是指向了函数的 prototype 属性。
从定义上看,对象拥有属性和方法,而prototype也拥有属性和方法,因此prototype看作一个对象,只不过是依附在函数的一个属性上面.那么我们可以称fn.prototype为原型对象.
prototype拥有一个属性constructor,它是一个指针,指向函数fn本身
一般情况下(除开Object.create()的特殊用法,比如传递进的对象是一个函数而不是函数的原型对象),__proto__永远指向一个函数的原型对象
function Foo() { }
let foo = new Foo()
console.log(foo.__proto__ == Foo.prototype, Foo.prototype,
Foo.prototype.__proto__ == Object.prototype, Foo.prototype.__proto__,
Foo.prototype.__proto__.__proto__ == null, Foo.prototype.__proto__.__proto__);
// true Foo {} true {} true null
// 原型链:foo.__proto__.__proto__.proto__==null
console.log(Foo.__proto__ == Function.prototype, Foo.__proto__,
Foo.__proto__.__proto__ == Object.prototype, Foo.__proto__.__proto__,
Foo.__proto__.__proto__.__proto__ == null, Foo.__proto__.__proto__.__proto__);
// true [Function] true {} true null
// 原型链:Foo.__proto__.__proto__.__proto__==null
构造函数的实例是一个对象,因此符合对象拥有__proto__属性而没有prototype属性的原则
prototype是每个函数创建的时候自带的默认属性,只有函数拥有,且存在于原型链上,其他的类型没有.即使其它类型自己定义一个prototype属性,这个属性也不会存在原型链上,只是一个普通的属性而已.
但是我们只要将一个实例对象(obj)连接到我们自己定义的原型上面,比如obj=Object.create(Obj.prototype)等方法使obj.__proto__=Obj.prototype,那么obj也可以通过原型链(__proto__)访问Obj.prototype上面定义的属性
let Person = {
name: 'avicii',
type:{}
}
let obj=Object.create(Person.type)
Person.type.hobby='music'
console.log(obj,obj.hobby,Person);//{} music { name: 'avicii', type: { hobby: 'music' } }
// 原型名称可以为任意名字,只要将实例obj.__type__=Person.type,那么obj就可以按照原型链的规则(通过__proot__查找)访问Person.type上面定义的属性
总结
以上例子可以看出原型链的本质就是实例对象通过__proto__这个隐式原型属性向后面的原型对象查找,即使一个对象(OBJ)不在原型的链条上,只要另一个对象(obj)的obj.__proto__=OBJ,那么obj就能以默认的原型链方法:通过__proto__访问的OBJ上面的属性和方法,但是因为OBJ不在原型链上(像举例代码中:OBJ=Person.type,type只是Person的属性,Person.type.__proto__没有链到原型链上),所以不能够访问原型链条上的其他属性和方法,但如果OBJ链到原型链上,那么obj就可以正常访问这个原型链上的属性和方法比如:
// function Say() {
// this.name = 'tim'
// }
// let Person = {
// name: 'avicii',
// type: {}
// }
// Person.type = Object.create(Say.prototype)
// Person.type.hobby = 'music'
// Say.prototype.face='cool'
// let obj = Object.create(Person.type)
// console.log(obj.hobby); //music
// 这里需要最后执行let obj = Object.create(Person.type),如果最开始执行这一句,那么obj.__proto__==Person.type实际上就是它们两者引用了在内存中的同一个地址
// 但是第二步执行的Person.type = Object.create(Say.prototype),会使Person.type引用一个空的Say.prototype对象在内存中的地址,Person.type引用内存中的地址发生了变化
// 这里直接是赋值不是引用, 所以obj.__proto__链接的Person.type在内存中的地址还是原来的地址,没有改变
//因此原型链其实是断开的,所以得出一个结论:原型链是从后面开始向前链接的,最前面的obj可以通过原型链访问后面的原型对象的属性和方法