摘要:在创建对象不论是普通对象还是函数对象的时候,都有一个叫做的内置属性,用于指向创建它的构造函数的原型对象,也就是。因为一个普通对象的构造函数所以原型链原型链的形成是真正是靠而非。参考文章最详尽的原型与原型链终极详解,没有可能是。
【前端芝士树】Javascript的原型、原型链以及继承机制
0. 为什么会出现原型和原型链的概念</>复制代码
前端的面试中经常会遇到这个问题,自己也是一直似懂非懂,趁这个机会整理一下
1994年,网景公司(Netscape)发布了Navigator浏览器0.9版,但是刚开始的Js没有继承机制,更别提像同时期兴盛的C++和Java这样拥有面向对象的概念。在实际的开发过程中,工程师们发现没有继承机制很难解决一些问题,必须有一种机制能将所有的对象关联起来。
Brendan Eich鉴于以上情况,但不想把Js设计得过为复杂,于是引入了new关键词 和 constructor构造函数来简化对象的设计,引入了prototype函数对象来包含所有实例对象的构造函数的属性和方法,引入了proto和原型链的概念解决继承的问题。
1. Javscript 函数和函数对象</>复制代码
var o1 = {};
var o2 =new Object();
var o3 = new f1();
function f1(){};
var f2 = function(){};
var f3 = new Function("str","console.log(str)");
</>复制代码
凡是直接或者间接通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。
在上面的程序中,o1、o2、o3都是普通对象,f1、f2、f3都是函数对象。
2. 构造函数</>复制代码
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() { alert(this.name) }
}
var person1 = new Person("Zaxlct", 28, "Software Engineer");
var person2 = new Person("Mick", 23, "Doctor");
</>复制代码
person1 和 person2 都是 构造函数 Person 的实例。
实例的构造函数属性(constructor)指向构造函数。
3. 原型对象
</>复制代码
Person.prototype = {
name: "Zaxlct",
age: 28,
job: "Software Engineer",
sayName: function() {
alert(this.name);
}
}
每个函数对象都有一个 prototype 属性,这个属性就是函数的原型对象。
</>复制代码
每个对象都有 proto 属性,但只有函数对象才有 prototype 属性
在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)
</>复制代码
Person.prototype.constructor == Person
而在Person这个对象进行实例化的时候,实际上是创建了一个它的实例对象并赋值给它的 prototype,所以得出以下结论:
</>复制代码
原型对象(Person.prototype)是 构造函数(Person)的一个实例。
4. _proto_
JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象,也就是prototype。
</>复制代码
Person.prototype.constructor == Person;
person1.__proto__ == Person.prototype;
person1.constructor == Person;
看下面一段代码
</>复制代码
Person.prototype.__proto__ === Object.prototype;
Person.prototype 是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。
因为一个 普通对象的构造函数 === Object
所以 Person.prototype.__proto__ === Object.prototype
</>复制代码
原型链的形成是真正是靠__proto__ 而非prototype。
</>复制代码
person1.__proto__ === Person.prototype;
Person.prototype.__proto__ === Object.prototype;
Object.prototype.__proto__ === null;
//Object.prototype.__proto__ === null,保证原型链能够正常结束。
Person.__proto__ === Function.prototype;
Object.__proto__ === Function.prototype;
Function.prototype.__proto__ === Object.prototype;
前面三项已经形成了一个原型链,那么后面代码中Person和Object的__proto__都是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
Object.__proto__ === Function.prototype // true
Object.constructor == Function // true
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
</>复制代码
所有的构造器都来自于 Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了·Function.prototype·的属性及方法。如length、call、apply、bind
所以我们再举一个原型链的例子
</>复制代码
let num = new Number();
num.__proto__ === Number.prototype;
Number.prototype.__proto__ === Function.prototype;
Funtion.prototype.__proto__ === Object.prototype;
Object.prototype.__proto__ === null;
也可看下图的实现,更直观。
ok, 所以明白为什么Number、String、Array这样的对象实例能继承到Object的属性以及原型链是怎么一回事了吧。
</>复制代码
参考文章
最详尽的 JS 原型与原型链终极详解,没有「可能是」。
三张图搞懂JavaScript的原型对象与原型链 - 水乙 - 博客园
Javascript继承机制的设计思想 - 阮一峰的网络日志
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/98270.html
摘要:没有找到的话,看上级函数作用域,向上查找到,找到为止。将会在执行上下文栈中保留上级作用域的执行上下文。若在闭包使用完毕之后不手动解除引用,相关执行上下文将会一直保留于执行上下文栈中,占据内存空间,若持续积累,容易造成内存泄漏。 JS有哪些基本数据类型呢? 值类型:undefined, Number, Boolean, String,null 引用类型:Object 值类型存放在栈中 引...
摘要:而像和会增加作用域链的长度,所以也会降低性能。但是用获取一些属性时,会不由自主地强迫队列中的所有渲染事件前不完成。在条件增加时,所带来的性能负担要高于,因此建议使用。它代价昂贵,且容易失控。 正巧看到在送书,于是乎找了找自己博客上记录过的一些东西来及其无耻的蹭书了~~~ 小广告:更多内容可以看我的博客 以下内容均来自《高性能JavaScript》 JavaScript文件加载 ...
摘要:而像和会增加作用域链的长度,所以也会降低性能。但是用获取一些属性时,会不由自主地强迫队列中的所有渲染事件前不完成。在条件增加时,所带来的性能负担要高于,因此建议使用。它代价昂贵,且容易失控。 正巧看到在送书,于是乎找了找自己博客上记录过的一些东西来及其无耻的蹭书了~~~ 小广告:更多内容可以看我的博客 以下内容均来自《高性能JavaScript》 JavaScript文件加载 ...
摘要:借用构造函数类式继承借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。 写在前面 【前端指南】前端面试库已经开源,正在完善之中 [x] css问题 [x] html问题 [x] javascript问题 github地址 https://github.com/nanhupatar... showImg(https://segmentfault.com/img...
1. JS基本的数据类型和引用类型 基本数据类型:number、string、null、undefined、boolean、symbol -- 栈 引用数据类型:object、array、function -- 堆 两种数据类型存储位置不同 原始数据类型是直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据; 引用数据类型存储在堆(heap)中的对象,占据空间大、大...
阅读 2415·2021-11-24 09:39
阅读 2639·2021-11-22 15:24
阅读 3054·2021-09-02 09:48
阅读 3116·2021-07-26 22:01
阅读 1512·2019-08-30 11:09
阅读 1753·2019-08-29 18:47
阅读 683·2019-08-29 15:40
阅读 2198·2019-08-29 15:22