【渣渣也写博客】我就这样看懂了原型和原型链

栏目: JavaScript · 发布时间: 5年前

内容简介:以下写法同 Person.prototype.constructor 直观地用数学思维理解:等号两边写谁都一样也就是说,原型对象里有一个叫constructor的属性,它指向自己的构造器(构造函数) 所以,一般用constructor属性来获取当前对象的构造函数那么好奇心来了,我想知道p1的构造函数是谁?这个问题很脑残,不过还是要打印一下:
__proto__(前后都是两条杠)
prototype
constructor
普通对象
函数对象

ps:本文是我写给自己看的,请慎读
复制代码

一. constructor prototype 和__proto__

每个对象都有__proto__ ,而prototype只有函数对象才有
复制代码
【渣渣也写博客】我就这样看懂了原型和原型链

1. 有时候当前对象没有我们想用的方法,所以我们就想看看它的原型上有木有,要怎么样才能获取到当前对象的原型呢?

答:__proto__  

上图中: obj.__proto__  === Object.prototype

首先 Object.prototype叫做obj的原型对象,这个对象里包含了所有对外共享的属性和方法。

其次,我们在控制台看到了很眼熟很扎眼的 constructor,它是什么鬼?
复制代码
【渣渣也写博客】我就这样看懂了原型和原型链

2. constructor 被包含在大括号{}中,说明它是原型对象中的一个属性。这个属性是干嘛的?请看以下代码

var function Person(name){
        this.name = name;
    }
    var p1 = new Person('林蛋大');
    
    console.log(p1.__proto__); //Person.prototype
    console.log(p1.__proto__ === Person.prototype); //true
复制代码

以下写法同 Person.prototype.constructor 直观地用数学思维理解:等号两边写谁都一样

console.log(p1.__proto__.constructor ); //Person
复制代码

也就是说,原型对象里有一个叫constructor的属性,它指向自己的构造器(构造函数) 所以,一般用constructor属性来获取当前对象的构造函数

那么好奇心来了,我想知道p1的构造函数是谁?这个问题很脑残,不过还是要打印一下:

console.log(p1.constructor); //Person
复制代码

纳尼?这是什么情况?岂不是我们有好多种获取构造函数的方法?Nice!

也就是说

p1.constructor === p1.__proto__.constructor
p1.constructor === Person.prototype.constructor
复制代码

p1这个对象是Person的一个实例,因为它是Person new 出来,它的constructor指向了Person;

Person.prototype也是一个对象,它的constructor也指向了Person,是不是可以理解为:Person.prototype 也是Person的一个实例?

实际上是真的可以这样理解的,我查阅了很多资料,虽然是伪相同,但网上大多数人都觉得这样更容易理解原型和原型链

中场小结:

  1. 通过__proto__可以获取原型,一直往一层层获取,一直__proto__直到结果为null就是尽头了。走完这个流程就是走完了你当前对象的原型链。(这一块后面细讲)

  2. 通过constructor可以获取当前对象的构造函数

  3. 任何对象都有原型,任何对象都可能是别的对象的原型,那么什么叫原型?通俗的理解: 原型是个对象,拿出来共享的属性和方法的集合。所有对象的__proto__ 都指向其构造器的 prototype。

  4. 在实际编码中会用到这三个属性做继承,这里有个坑,如下:

function Person(name) {
    this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true


// 重写原型
Person.prototype = {
    getName: function() {}
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false

????

复制代码

这是因为给Person.prototype赋值的是一个对象直接量{getName: function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,所以---

这个时候要改很简单,只要把constructor给指回来就好了,这是写原型继承的时候最最最重要的!

Person.prototype = {
    getName: function() {}
}
var p = new Person('jack')
p.constructor = Person;
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false
复制代码

二. 原型链

上节说到__proto__可以获取原型,一直往上获取就能拿到整条链子,口说不清我们直接看代码:

var function Person(name){
        this.name = name;
    }
var p1 = new Person('楚中天');

//不打印的话,你知道以下会输出什么吗?
1. p1.__proto__  ?
2. Person.__proto__   ?
3. Person.prototype.__proto__  ?

复制代码
  1. 肯定能秒答:Person.prototype
  2. 也就是构造函数Person的原型,既然是函数,那么肯定是Function.prototype 此处你的脑子需要有一条链子的形象:
【渣渣也写博客】我就这样看懂了原型和原型链

在js中,数组是对象,函数是对象,正则是对象,日期是对象..... 大家都说在javasript中“万物皆对象”,是什么意思呢?

console.log(p1.arguments) // arguments 从哪里来的?
console.log(f.call(window)) // call 方法从哪里来的?
复制代码

相信大家也看过很多代码,经常突然冒出arguments,找遍全世界也没有找到arguments是在哪里声明的。毕竟p1中并没有arguments属性,找到它的原型Person,上面也没有,哪儿冒出来的?

console.log(Function.prototype) // function() {} (一个空的函数)
console.log(Object.getOwnPropertyNames(Function.prototype)); 
/* 输出
["length", "name", "arguments", "caller", "constructor", "bind", "toString", "call", "apply"]
*/
复制代码

Person没有,Person的上级Function有鸭,同理,getOwnPropertyNames也是Function没有,它的上级Object的prototype对象里有。

打个比方就是p1的家族世代修炼,每一代都修炼出自己的独门绝学,然后传给后代,最后面的p1就继承到了以上所有祖宗的绝学(属性和方法)

在JS的世界里大家都是分散的对象,总有个机制把他们连接起来才能去运转去工作去产出,所以js作者设计了这一套继承机制,这也就是所谓的原型链。

所以,数组和函数都能用顶级公民Object.prototype里的属性和方法,大家自行打印一下,方法和属性非常多。

看到这里大家肯定有个疑问:Function.prototype 为什么是个空函数?请看下一节

三. 特殊公民 Function.prototype

所有函数对象的__proto__都指向Function.prototype,也就是说它是所有函数对象的原型对象。而且它是一个空函数(Empty function)

Number.__proto__ === Function.prototype  // true
Number.constructor == Function //true
 
Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true
 
String.__proto__ === Function.prototype  // true
String.constructor == Function //true
 
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Object.__proto__ === Function.prototype  // true
Object.constructor == Function // true
 
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true
 
Array.__proto__ === Function.prototype   // true
Array.constructor == Function //true
 
RegExp.__proto__ === Function.prototype  // true
RegExp.constructor == Function //true
 
Error.__proto__ === Function.prototype   // true
Error.constructor == Function //true
 
Date.__proto__ === Function.prototype    // true
Date.constructor == Function //true

复制代码

JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的proto是Object.prototype。如下

Math.__proto__ === Object.prototype  // true
Math.construrctor == Object // true
 
JSON.__proto__ === Object.prototype  // true
JSON.construrctor == Object //true
复制代码

上面说的函数对象当然包括自定义的。如下

// 函数声明
function Person() {}
// 函数表达式
var Perosn = function() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype)    // true
复制代码

这些说明所有的构造器都来自于 Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了·Function.prototype·的属性及方法。 所以,函数是唯一一个typeof 结果是function 的类型。

那么上一节的问题,说Function.prototype怎么是个空函数啊?不止是它,如果你求一个数组的原型,也是一个空数组:

console.log(Function.prototype) // function() {} (一个空的函数)
console.log(Array.prototype) // [ ] (一个空的数组)

复制代码

怎么办?故技重施啊!它自己没有,它的原型肯定有鸭!

//首先 Function.prototype/Array.prototype是个对象,对象的上级自然是对象的原型

console.log(Function.prototype.__proto__  === Object.prototype ) //true
console.log(Array.prototype.__proto__  === Object.prototype ) //true

//Object.prototype的原型呢?null,到世界的尽头了
console.log(Object.prototype.__proto__  === null )//true

复制代码

到世界尽头了?刚才明明说object也是function 创建的,那么Object的原型对象是谁__proto__指向谁?

Object.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype // true
//上面这个两个说明万物的祖宗Object还有特殊公民的原型对象都是Function.prototype


//这个就有点迷了
Function.prototype.__proto__ === Object.prototype //true
就连函数它自己也是从Function.prototype出世的,按理说它的原型应该指向它自己,为什么会指向Object.prototype ?

不过如果指向它自己,不就是死循环,这条链子没完没了了?而且还没多大意义
所以指向Object.prototype,而Object.prototype 指向null, 整条链子结束。

复制代码

四. 灵魂面试题

function Person(name) {
    this.name = name
}
var p2 = new Person('king');

核心点:__proto__是求原型对象带prototype的 ===>是构造器的一个属性,本身是个对象
        
     constructor 是求构造器的 ====> 构造器的prototype对象里也有它,指向构造器自己

console.log(p2.__proto__)//Person.prototype
console.log(p2.__proto__.__proto__)//结合上题,也就是Person.prototype的__proto__,Person.prototype本身是个对象,所以这里输出:Object.prototype
console.log(p2.__proto__.__proto__.__proto__)//同理,这里是求Object.prototype的__proto__,这里输出:null
console.log(p2.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错
console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错

console.log(p2.constructor)//Person
console.log(p2.prototype)//undefined p2是实例对象,不是函数对象,是没有prototype属性滴

console.log(Person.constructor)//空函数(其实是初始的函数构造器)
console.log(Person.prototype)//打印出Person.prototype这个对象里的所有方法和属性

console.log(Person.prototype.constructor)//Person
console.log(Person.prototype.__proto__)//Person.prototype是对象,所以输出:Object.prototype
console.log(Person.__proto__)//Function.prototype

console.log(Function.prototype.__proto__)//Object.prototype
console.log(Function.__proto__)//Object.prototype

console.log(Object.__proto__)//Function.prototype
console.log(Object.prototype.__proto__)//null

复制代码

以上所述就是小编给大家介绍的《【渣渣也写博客】我就这样看懂了原型和原型链》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

.NET本质论 第1卷:公共语言运行库

.NET本质论 第1卷:公共语言运行库

博克斯 (BoxDon) / 张晓坤 / 中国电力出版社 / 2004-1 / 48.00元

本书由10章组成,探讨了CLR即公共语言运行库,涵盖了基本类型、实例、方法调用和消息、AppDomain、安全、以及CLR外部世界。一起来看看 《.NET本质论 第1卷:公共语言运行库》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具