前端三大山之一:原型链
参照了思否Sunshine_Lin的文章:https://segmentfault.com/a/1190000041095282
prototype和__proto__
- prototype: 显式原型
- __ proto__: 隐式原型 【注意proto前后分别是两条 _ 】
一般构造函数的prototype和其原型的__proto__指向的是同一个地方,叫做原型对象
构造函数
通俗的来讲,构造函数就是可以用来new的函数,箭头函数不能当做构造函数
function Person(name,age){ // 这就是构造函数 name,age 是传的参数 this.myname 是Person的属性,把参数值赋予属性 this.myname = name this.myage = age } Person.prototype.sayName = function(){ console.log('在下'+this.myname,'今年'+this.myage+'岁') } const p1 = new Person('明世隐',10)// 这是 Person 的实例 const p2 = new Person('李白',20) // 这是 Person 的实例 console.log(p1)//Person { myname: '明世隐', myage: 10 } console.log(p2) // Person { myname: '李白', myage: 20 } console.log(p1.myname) // 明世隐 console.log(p2.myname)//李白 p1.sayName()//在下明世隐 今年10岁 p2.sayName()//在下李白 今年20岁 console.log(p1.__proto__)// Person{sayName:[function]} console.log(Person.prototype) // Person{sayName:[function]} console.log(p1.__proto__ === Person.prototype) // true // 总结:构造函数的proptotype 与实例的 __proto__指向的是同一个地方 叫做原型对象
函数
构造函数也是一个函数,定义函数有以下几个方法
// 构造函数说到底也是一个函数,比如平时定义函数 function fn1(name,age){ console.log(`在下${name},今${age}`) } const fn2 = function(name,age){ console.log(`在下${name},今${age}`) } const fn3 = (name,age)=>{ console.log(`在下${name},今${age}`) } // 除此之外,这三个函数都可以用 new Function 来声明,所以 Function 也是一个构造函数 上面的写法等同于 const fn4 = new Function('name','age','console.log(`在下${name},今${age}`)') fn3('卤蛋',2)//在下卤蛋,今2 fn4('卤蛋',3)//在下卤蛋,今3 // 所以 fn1\fn2\fn3\fn4都是 Function 构造函数的实例 console.log(fn1.__proto__ === Function.prototype)// true 其他函数一样的就不多打印了
对象
// 一般创建对象有以下几种方式: // 构造函数创建对象,创建出来的都是此Function构造函数的实例 function Hero(name,age){ this.name = name this.age = age } const hero1 = new Hero('诸葛亮',13) // 字面量创建对象 本质就是 new Object() 创建对象 const hero2= {name:'澜',age:12} // new Object 创建对象 const hero3 = new Object() hero3.name = '澜' hero3.age = 12 // Object.create 创建对象,创建出来的是空原型对象 const hero4 = Object.create({}) hero4.name = '后羿' hero4.age = 34 // 所以 hero2,hero3都是object构造函数的实例 console.log(hero2.__proto__ === Object.prototype)// true
console.log(hero3.__proto__ === Object.prototype)// true
【图不是原创的,所以跟我自己写的例子不一致,反正我没怎么看懂,就先放着吧】
Function和Object
函数
是Function构造函数
的实例对象
是Object构造函数
的实例
function Object() 和 function Function() 其实都是函数,所以他们都是Function构造函数的实例。【要注意名称,可以顾名思义,比如原型对象,本质是对象】
console.log(Function.prototype === Function.__proto__)// true console.log(Function.prototype === Object.__proto__) // true
constructor
作者原话:constructor和prototype是成对的,你指向我,我指向你。举个例子,如果你是我老婆,那我肯定是你的老公。
function fn(){} console.log(fn.prototype)// fn {} console.log(fn.prototype.constructor)//[Function: fn] console.log(fn === fn.prototype.constructor)//true
原型链
// 什么是原型链?通俗的说,__proto__的路径就是原型链 console.log(Person.prototype)//指向原型对象 Person { sayName: [Function] } 是一个对象 ,对象是Object构造函数的实例,所以如下 console.log(Person.prototype.__proto__ === Object.prototype) // true console.log(Person.__proto__)//[Function],Person 的原型是Function,Person的实例是对象 console.log(Person.__proto__ === Function.prototype) // true ,Person 是Functtion构造函数的实例,Person.prototype 是Object构造函数的实例 console.log(p1.__proto__.__proto__ === Object.prototype) //true //总结:Person.__proto__指向Person的原型(向上,指向上一层),Person.prototype指向Person的实例的原型(向下,指向下一层),而Person 是一个构造函数,function构造函数是Function 的实例,个人为了方便记忆瞎总结出来的,不一定对。
原型链终点
上面咱们看到,三条原型链结尾都是Object.prototype
,那是不是说明了Object.prototype
就是原型链的终点呢?其实不是的,Object.prototype
其实也有__proto__,指向null,那才是原型链的终点
完整图如下
原型继承
创建了一个构造函数之后,通过new 实例化对象,每一个实例都是独立的互相不影响。【要自己敲过代码后才能更好的理解】
// 先创建一个构造函数 function Hero(name,age){ this.name = name this.age = age } //实例只有调用了setPosition才会执行该方法,增加一个新属性position,和更改原有的属性age,但是对于另外一个实例hero11的属性值不会有影响,也不会更改hero11的属性age
//【注意实例的名称,hero1和hero11,因为做练习的时候同一个文件里已经有hero2了,为避免重复就用了hero11】 Hero.prototype.setPosition = function(){ //往原型对象添加方法 this.position = '刺客' this.age = 18 console.log(`${this.name},今年${this.age},定位:${this.position},要做的是${this.doWhat},其他信息${this.otherMsg}`) } Hero.prototype.doWhat = '打野' console.log(Hero.prototype) // Hero { setPosition: [Function], doWhat: '打野' } let hero1 = new Hero('兰陵王',33) let hero11 = new Hero('阿轲',23) hero1.age = 20 console.log(hero1.doWhat)// 打野 原型对象里面的属性 直接打印实例是不会展示出来的,但是通过调用 hero1.doWhat 可以拿到 console.log(hero11.doWhat)// 打野 hero1.setPosition() // 兰陵王,今年18,定位:刺客,要做的是打野 console.log(hero1)//Hero { name: '兰陵王', age: 18, position: '刺客' } console.log(hero11)//Hero { name: '阿轲', age: 23 } 这里没有position 是因为它没有调用 setPosition(),position是在setPosition方法里面新增的一个属性,只有调用才会生效 // 实例如何更改自己的属性都不会影响原型对象或者其他的实例 hero1.doWhat = '抓单' hero1.otherMsg = '没有了' console.log(hero1.doWhat)//抓单 console.log(hero11.doWhat)//打野 hero1.setPosition()//兰陵王,今年18,定位:刺客,要做的是抓单,其他信息没有了 console.log(hero1)//Hero { name: '兰陵王', age: 18, position: '刺客', doWhat: '抓单' } console.log(hero11)// Hero { name: '阿轲', age: 23 } hero11.setPosition()//阿轲,今年18,定位:刺客,要做的是打野,其他信息undefined console.log(Hero.prototype)//Hero { setPosition: [Function], doWhat: '打野' }
instanceof
使用方法: a instanceof b 判断b的prototype是否在a的原型链上
function ADC(name){ this.name = name } const adc = new ADC('李元芳') //ADC 是一个构造函数,既然是函数,那它就是Function构造函数的实例 //ADC.prototype 是构造函数ADC的原型对象,既然是对象,那它就是Object构造函数的实例 //adc的原型链:adc = new ADC() ——[.__proto__] ——>ADC.prorotype ——[.__proto__]——> Object.prototype // ADC的原型链:ADC = new Function() ———[.__proto__] ——> Function.prorotype ——[.proto__] ——>Object.prototype console.log(ADC instanceof Function) // true console.log(ADC instanceof Object) //true console.log(adc instanceof ADC) // true console.log(adc instanceof Object)// true console.log(adc instanceof Function)//false
一些练习题
//*********没搞懂 */ function B(a){ this.a = a } B.prototype.a = 1 const b = new B() console.log(b.a)//undefined 为什么是undefined // 猜测:原本B原型上是有a:1的,因为B.prototype.a = 1,但是b 是B的实例化对象,B实例化之后会把传入的参数赋值给原来a的值,但是这里实例化没传值,所以this.a = undefined,所以实例化的b里面的a=undefined,而原型B里的a=1 console.log(B.prototype)// B { a: 1 } //********* //123是数字,其本质是 new Number(),数字本身没有toString方法 console.log(123['toString'].length + 123)
123是数字,数字本质是new Number()
,数字本身没有toString
方法,则沿着__proto__
去function Number()
的prototype
上找,找到toString方法,toString方法的length是1,1 + 123 = 124
,至于为什么length是1,可以看95%的人都回答不上来的问题:函数的length是多少?