原型链深度
前言
我经常搞混一个概念,在读文章前一定要记得这句话:并不是所以对象都有prototype,通常只有函数有prototype对象,但是每个对象都有一个__proto__。 原型链的查找过程始终都是沿着__proto__向上找的。构造函数的原型链之所以会去prototype上找是因为被构造函数new出来的对象的__proto__指向的是构造函数的prototype,(换个说法就是被构造函数new出来的对象的__proto__=构造函数的prototype。叫指向或者等于我觉得都可以)
一 如何指定原型链
1、普通构造函数
准备一个对象和函数,如下,然后手动的将构造函数的prototype指定对象。
var obj = {b:function(){}}
function Foo(){
this.name = '蓉蓉'
}
// 默认情况Foo.prototype上有一个
//constuctor:Foo.prototype = { constuctor: Foo }
// 正常情况添加原型的方法:Foo.prototype.a = function(){}
// 手动设置原型对象:Foo.prototype = obj
var a = new Foo()
// a = {
// name:'蓉蓉',
// __proto__: Foo.prototype //{b:function(){}}
}
这时当别人new Foo时,就会构造出一个新的对象(例:a),a里面会默认有个proto的属性指向Foo.prototype,
(补充:Foo.prototype默认是一个对象,并且默认的对象里有个constuctor属性,constuctor的值指向该原型对象对应的构造函数 (例:Foo(){}),但是上面例子中,由于我们手动的将Foo.prototype指向了另一个新对象,新对象里也并没有给出constuctor的属性,((注:当然可以选择给这个新对象的增加一个constuctor属性并且赋值))这时就会出现构造出来的a在访问constuctor时到原型链(Foo.prototype)上找,但是因为Foo.prototype上没有constuctor这个属性,那么就会继续向更深的原型链上去找,直到找到Object为止。
补充知识点:所有的构造函数最后都会隐形的将this return出去,我们平时没有写只是js默认帮我们干了return这件事,按照这个推理我们可以人工的干预return出去的对象,举个栗子:
function Foo(){
this.name = '蓉蓉'
return { name: '我是浏览'}
}
var a = new Foo()
// a = {
name: '我是浏览'
__proto__:Foo.prototype // {}
}
2、使用Object.create()指定原型
var obj = {bb:1}
var a = Object.create(obj)
//a = {
// __proto__:{ bb :1}
//}
a.rr = 1;
var b = Object.create(a)
// b = {
// __proto__:{
// rr:1,
// __proto__:{bb:1
// }
// }
//}
b.constuctor // Object(){}
由于没有指定constuctor,所以是由Object构造函数构造
上面这种写法是最简单的持续继承的方式,祖宗十八代的原型链,简洁易懂,没啥子好说的
3、es6的extends指定原型
class A{
static getname(){ }
rr(){}
}
class B extends A{
}
//转换成es5
function A(){
}
A.prototype ={
constructor:A,
rr:function(){}
}
function B(){
}
B.__proto__ = A
B.prototype = new A()
// 运行结果
B.prototype ={
__proto__:{
constructor:function A(){},
rr::function(){}
}
}
B.prototype.constructor = B
需要补充的知识点B.prototype = new A()这句话可以看出B.prototype 被赋值给了一个新的对象,并且这个新对象里没有constructor这个这个属性,只有原型proot指针上有一个constructor是指向A的,B.prototype.constructor = B这句话,为了保证被B类new出来的对象原型链能正常找B祖宗,所以需要指定B.prototype.constructor =B;但是在extends中会自动将这种关系指定好,另外注意到有这么一句话:
B.proto= A,为什么会写这个呢,是class中定义了一个静态方法,getname,这个方法是直接挂在A上的,正常的new的时候实例是无法继承的,但是类有的方法,继承的类也是可以访问的,我一直很好奇静态方法继承的类是如何继承的,(只是访问父级类,并不会给继承的类也生成一份)这个地方曾让我一度绕晕,__proto__不是通常都指的是一个prototype,是一个对象吗,怎么直接指的是一个类了?
function B()
// 默认B的prototype是这样的
B.prototype={
constructor : B,
__proto__: Object.prototype
}
但是其实B还有
const cat1 = new B()
// cat1等于是
cat1={
__proto:B.prototype
}
从这里可以看出__proto__指向的是说是谁把你new出来的那个的prototype,所以当你找不到方法时可以顺着
原型一直向原型上找,但是其实function B(),B本身其实也是有__proto__这个属性的,可怕的是我之
前一直以为没有,因为B是被new Function弄出来的,所以这样的话就是:
B._proto__ = Function
所以B如果直接写B.xx以点啥访问一个属性时访问不到会去Function这个类里找,注意哦,这个B是一个类了,不
是实例,我老是会混掉,那上面的例子种B是extends继承A的,所以关系是:
B_proto__ = A
A__proto__ = Function
4、es6类的基础知识
class A{
coco = ()=>{}
bar(){}
render(){
return <div onClick={this.bar.bind(this)}></div>
}
}
coco:
coco是箭头函数默认绑定外部环境的this,所以在类里面这么写等于于是写在构造函数的this上的,每次在new这个类的时候都会生成一个coco函数
bar:
bar函数在类里面这么写函数等于是写在构造函数的prototype上的,当然new的时候不会重新生成一个bar,但是因为没有为其绑定this,所以通常在写react的时父组件向子组件传递绑定事件时需要手动绑定this指明执行时this指向父级,而不是执行环境时的this
二 关于instanceof
我总是会忘记掉instanceof是干嘛的,希望能记在这个地方,加深我的记忆。instanceof与typeof类似,都是用来判断一个变量是什么类型。但是typeof通常就返回固定的几种类型:number,boolean,string,function,object,undefined
在判断对象的时候比较局限。而instanceof还可以返回一个变量是否某个对象的实例,比如:
var a=new Array();
a instanceof Array // 会返回 true,
// 同时
a instanceof Object // 也会返回 true;
//这是因为 Array 是 object 的子类。再如:
function test(){};
var a=new test();
a instanceof test //会返回 true
重点:
总结instanceof还可以判断一个变量是否某个对象的实例
该文章为个人学习笔记,有很多不严谨的地方