javascript系列–认识并理解构造函数,原型和原型链

一、前言

介绍构造函数,原型,原型链。比如说经常会被问道:symbol是不是构造函数;constructor属性是否只读;prototype、[[Prototype]]和proto的区别;什么是原型链?等等问题

二、构造函数

1、什么构造函数

构造函数就是通过new关键词生成实例的函数。

js的构造函数和其他语言不一样,一般规范都是首字母大写。

首先我们来看一下这个栗子:

// saucxs function Parent(age) {     this.age = age; } var p = new Parent(30); console.log(p);  //见下图 console.log(p.constructor);  // ƒ Parent(age){this.age = age;} p.constructor === Parent; // true p.constructor === Object; // false 
javascript系列--认识并理解构造函数,原型和原型链
image

这就是一个典型的构造函数,构造函数本身也是个函数,与普通区别不大,主要区别就是:构造函数使用new生成实例,直接调用就是普通函数。

2、constructor属性

返回创建实例对象的Object构造函数的引用。此属性的值对函数本身的引用,而不是一个包含函数名称的字符串。

所有对象都会从它的原型上继承一个constructor属性

var o = {}; o.constructor === Object; // true  var o = new Object; o.constructor === Object; // true  var a = []; a.constructor === Array; // true  var a = new Array; a.constructor === Array // true  var n = new Number(3); n.constructor === Number; // true 

那么普通函数创建的实例有没有constructor属性呢?

// saucxs // 普通函数 function parent2(age) {     this.age = age; } var p2 = parent2(50); console.log(p2); // undefined  // 普通函数 function parent3(age) {     return {         age: age     } } var p3 = parent3(50); console.log(p3.constructor);  //ƒ Object() { [native code] } p3.constructor === parent3;  // false p3.constructor === Object; // true 

上面代码说明:

(1)普通函数在内部有return操作的就有constructor属性,没有return的没有constructor属性;

(2)有constructor属性的普通函数的constructor属性值不是普通函数本身,是Object。

3、symbol是构造函数吗?

MDN 是这样介绍 Symbol

The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax "new Symbol()".

Symbol是基本数据类型,作为构造函数它不完整,因为不支持语法new Symbol(),如果要生成实例直接使用Symbol()就可以的。

// saucxs new Symbol(123); // Symbol is not a constructor  Symbol(123); // Symbol(123) 

虽然Symbol是基本数据类型,但是Symbol(1234)实例可以获取constructor属性值。

// saucxs var sym = Symbol(123);  console.log( sym );  // Symbol(123)  console.log( sym.constructor ); // ƒ Symbol() { [native code] } sym.constructor === Symbol;  //true sym.constructor === Object;  //false 

这里的constructor属性来自哪里?其实是Symbol原型上的,默认为Symbol函数。

4、constructor的值是只读的吗?

回答:如果是引用类型的constructor属性值是可以修改的,如果是基本类型的就是只读的。

引用类型的情况,修改这个很好理解,比如原型链继承的方案中,就是对constructor重新赋值的修正。

// saucxs function Foo() {     this.value = 42; } Foo.prototype = {     method: function() {} };  function Bar() {}  // 设置 Bar 的 prototype 属性为 Foo 的实例对象 Bar.prototype = new Foo(); Bar.prototype.foo = 'Hello World';  Bar.prototype.constructor === Object;  //true  
// 修正 Bar.prototype.constructor 为 Bar 本身 Bar.prototype.constructor = Bar;  var test = new Bar() // 创建 Bar 的一个新实例 console.log(test); 
javascript系列--认识并理解构造函数,原型和原型链
image

对于基本类型来说是只读的,比如:1, "saucxs", true, Symbol, null, undefined。null和undefined也是没有constructor属性的。

// saucxs function Type() { }; var types = [1, "muyiy", true, Symbol(123)];  for(var i = 0; i < types.length; i++) {     types[i].constructor = Type;     types[i] = [ types[i].constructor, types[i] instanceof Type, types[i].toString() ]; };  console.log( types.join("n") ); // function Number() { [native code] },false,1 // function String() { [native code] },false,muyiy // function Boolean() { [native code] },false,true // function Symbol() { [native code] },false,Symbol(123) 

为什么会这样?因为创建他们的是只读的原生构造函数(native constructors),这个栗子说明依赖一个对象的constructor属性并不安全。

三、原型

3.1 ****prototype属性

每一个对象都拥有一个原型对象,对象以其原型为模板,从原型集成方法和属性,这些属相和方法都在对象的构造器函数的prototype属性上,而不是对象实例本身上。

javascript系列--认识并理解构造函数,原型和原型链
image

上图发现:

1、Parent对象有一个原型对象Parent.prototype,原型对象上有两个属性,分别为:constructor和proto,其中proto已被弃用。

2、构造函数Parent有一个指向原型的指针constructor;原型Parent.prototype有一个指向构造函数的指针Parent.prototype.constrcutor,其实就是一个循环引用。

javascript系列--认识并理解构造函数,原型和原型链
image

3.2 proto****属性

上图中可以看到Parent原型(Parent.prototype)上有一个proto属性,这是一个访问器属性(即getter函数和setter函数)。作用:通过proto可以访问到对象的内部[Prototype]

__proto__ 发音 dunder proto,最先被 Firefox使用,后来在 ES6 被列为 Javascript 的标准内建属性。

[[Prototype]] 是对象的一个内部属性,外部代码无法直接访问。

// saucxs function Parent(){};  var p = new Parent(); console.log(p); console.log(Parent.prototype); 
javascript系列--认识并理解构造函数,原型和原型链
image

1、p.proto获取的是对象的原型,proto是每一个实例上都有的属性

2、prototype是构造函数的属性;

3、p.proto和Parent.prototype指向同一个对象。

// saucxs function Parent() {} var p = new Parent(); p.__proto__ === Parent.prototype // true 

所以构造函数Parent,Parent.prototype和p之间的关系,如下图所示:

javascript系列--认识并理解构造函数,原型和原型链
image

注意1:__proto__ 属性在 ES6 时才被标准化

以确保 Web 浏览器的兼容性,但是不推荐使用,除了标准化的原因之外还有性能问题。为了更好的支持,推荐使用 Object.getPrototypeOf()

如果要读取或修改对象的 [[Prototype]] 属性,建议使用如下方案,但是此时设置对象的 [[Prototype]] 依旧是一个缓慢的操作,如果性能是一个问题,就要避免这种操作。

如果要创建一个新对象,同时继承另一个对象的 [[Prototype]] ,推荐使用 Object.create()

// saucxs function Parent() {     age: 50 }; var p = new Parent(); var child = Object.create(p); 

这里 child 是一个新的空对象,有一个指向对象 p 的指针 __proto__

四、原型链

每一个对象拥有一个原型对象,通过proto指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层层的,最终指向null。这种关系成为原型链(prototype chain),作用:通过原型链一个对象会拥有定义在其他对象中的属性和方法

// saucxs function Parent(age) {     this.age = age; }  var p = new Parent(50); p.constructor === Parent; // true 

p.constructor指向Parent,那么是不是意味着p实例化存在constructor属性呢?并不存在,打印一下p:

javascript系列--认识并理解构造函数,原型和原型链
image

有图可以知道,实例化对象p本身没有constructor属性,是通过原型链向上查找proto,最终找到constructor属性,该属性指向Parent

// saucxs function Parent(age) {     this.age = age; } var p = new Parent(50);  p;  // Parent {age: 50} p.__proto__ === Parent.prototype; // true p.__proto__.__proto__ === Object.prototype; // true p.__proto__.__proto__.__proto__ === null; // true 

下图展示原型链运行机制。

javascript系列--认识并理解构造函数,原型和原型链
image

五、总结

1、Symbol是基本数据类型,作为构造函数并不完整,因为不支持语法new Symbol(),但是原型上拥有constructor属性,即Symbol.prototype.constructor。

2、引用类型constructor属性值是可以修改的,但是对于基本类型的是只读的,当然null和undefined没有constructor属性。

3、proto是每个实例上都有的属性,prototype是构造函数的属性,这两个不一样,但是p.proto和Parent.prototype是指向同一个对象。

4、proto属性在ES6时被标准化,但是因为性能问题并不推荐使用,推荐使用Object.getPropertyOf()。

5、每个对象拥有一个原型对象,通过_ptoto指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层已成的,最终指向null,这就是原型链。

原文始发于:javascript系列--认识并理解构造函数,原型和原型链

0

nmgwap

我还没有学会写个人说明!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

微信扫一扫

微信扫一扫

微信扫一扫,分享到朋友圈

javascript系列–认识并理解构造函数,原型和原型链
返回顶部

显示

忘记密码?

显示

显示

获取验证码

Close