【渣渣也写博客】我就这样看懂了原型和原型链
栏目: JavaScript · 发布时间: 6年前
内容简介:以下写法同 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的一个实例?
实际上是真的可以这样理解的,我查阅了很多资料,虽然是伪相同,但网上大多数人都觉得这样更容易理解原型和原型链
中场小结:
-
通过__proto__可以获取原型,一直往一层层获取,一直__proto__直到结果为null就是尽头了。走完这个流程就是走完了你当前对象的原型链。(这一块后面细讲)
-
通过constructor可以获取当前对象的构造函数
-
任何对象都有原型,任何对象都可能是别的对象的原型,那么什么叫原型?通俗的理解: 原型是个对象,拿出来共享的属性和方法的集合。所有对象的__proto__ 都指向其构造器的 prototype。
-
在实际编码中会用到这三个属性做继承,这里有个坑,如下:
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__ ?
复制代码
- 肯定能秒答:Person.prototype
- 也就是构造函数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
复制代码
以上所述就是小编给大家介绍的《【渣渣也写博客】我就这样看懂了原型和原型链》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Agile Web Application Development with Yii 1.1 and PHP5
Jeffrey Winesett / Packt Publishing / 2010-08-27
In order to understand the framework in the context of a real-world application, we need to build something that will more closely resemble the types of applications web developers actually have to bu......一起来看看 《Agile Web Application Development with Yii 1.1 and PHP5》 这本书的介绍吧!