摘要:原型链构造函数原型实例的关系每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,实例有一个指向原型对象的指针构造函数原型对象构造函数构造函数操作符实例对象构造函数实例对象原型对象如果试
原型链
构造函数/原型/实例 的关系
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,实例有一个指向原型对象的指针
构造函数 --(prototype)--> 原型对象 --(constructor)--> 构造函数
构造函数 --(new 操作符)--> 实例对象 --(constructor)--> 构造函数
实例对象 --(__proto__)--> 原型对象
如果试图使用某个对象(实例)的某个属性或方法,会首先在对象内部寻找该属性,如果找不到去该对象的原型(instance.__proto__)里面去找,如果还找不到就继续沿着 __proto__ 这个链条往上找,直到找到 Object.prototype 为止
javascript 里面一切皆对象,所以都可以从这个链条去出发
JavaScript 的继承不同于传统面向对象是靠类实现继承,而是通过原型链实现继承
ES5 继承拷贝式继承(通过深拷贝实现继承)
原型式继承
缺点:只能继承原型方法
借用构造函数继承
缺点:只能继承实例属性
组合式继承
缺点:无论在什么情况下,都会调用两次构造函数(创建父类实例的时候,在子类构造函数内不调用父类构造函数时)
组合寄生式继承 (比较完美的继承,但不能继承父类静态方法、静态属性)
function Parent() {} function Child() { // 继承父类实例属性 Parent.call(this) // 如果父类有参数写在 this 后面 } // 继承父类原型方法 Child.prototype = Object.create(Parent.prototype) // 修正子类原型的 constructor 指向 Child.prototype.constructor = Child
Object.create(proto, [propertiesObject]) MDN
创建一个新对象,使用现有的对象来提供新创建的的对象的 __proto__ Object.create(null) // 创建一个没有原型的空对象 第二个参数可添加属性描述符 js高级程序设计用一下代码代替的这个方法 function createObject(P) { var F = function() {} F.prototype = P.prototype return new F() }
为什么要修正子类原型的 constructor 指向? 阮一峰
简单总结一下: 任何一个 prototype 对象都有一个 constructor 属性,指向它的构造函数 更重要的是,每一个实例也有一个 constructor 属性,默认调用 prototype 的 constructor 属性 如果没有修正的那行代码,结果如下 var c = new C() c.constructor === Child // false Child.prototype.constructor === Child // false c.constructor === Parent // true Child.prototype.constructor === Parent // true 这显然会导致继承链的混乱(c 明明是用构造函数Child生成的),因此我们必须手动纠正ES6 继承
ES6 的继承本质上还是借助原型链实现继承
// 借助 class extends 关键字 class Parent { static sayAge() { return "18" } constructor(name) { this.name = "name" } } class Child extends Parent { constructor(name, age) { /** * 如果写 constructor 必须调用 super 方法,这是因为子类自己的 this 对象,必须先通过父类构造函数完成塑造 * 不写 super 就得不到 this对象,new 的时候就会报错 * Uncaught ReferenceError: Must call super constructor in derived class before accessing "this" or returning from derived constructor * * ES5 实质上先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面 (Parent.call(this)) * ES6 实质上先将父类实例对象的属性和方法,加到 this 上面(必须先调用 super 方法),然后用子类构造函数修改 this */ super(name, age) this.age = age } } // 注意点:es6 的继承可以继承父类的静态方法和静态属性,而ES5的继承不行 // 经过 extends 之后,Child.__proto__ ==== Parent // true // Parent.__proto__ 返回 f() { [native code] } // Parent.__proto__.__proto__ === Object.prototypeBabel 转码后的 ES6 继承代码
// 为方便观看,对代码做了一些美化和省略处理 "use strict"; // 检查子类是否调用了 super 方法 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError( "this hasn"t been initialised - super() hasn"t been called" ); } return self; } // 获取子类的原型链指向的对象即父类 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } // 核心继承方法 function _inherits(subClass, superClass) { // ... // 同 es5 继承的那一段 subClass.prototype = Object.create(superClass.prototype, { constructor: { value: subClass, // 修正 constructor 指向 writable: true, configurable: true } }); // 实现静态属性和方法的继承 原理为:Child.__proto__ = Parent // 即子类(子类现在相当于实例)的在往上的 prototype = Parent,即子类可以使用父类的静态属性和方法 if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } // ... 省略类的创建及检测等方法 var Parent = /*#__PURE__*/ (function() { _createClass(Parent, null, [ { key: "sayAge", value: function sayAge() { return "18"; } } ]); function Parent(name) { _classCallCheck(this, Parent); this.name = "name"; } return Parent; })(); var Child = /*#__PURE__*/ (function(_Parent) { _inherits(Child, _Parent); function Child(name, age) { var _this; _classCallCheck(this, Child); // 下面一行代码即 调用 super 的效果,如果不调用 super 子类将没有 this _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name, age)); /*** * 如果注释源码中的 super 将编译为如下代码 * _this.age = age; * return _possibleConstructorReturn(_this); * 因为没有调用 super 子类还没有 this,所以下一行直接报错 * * 如果源码中不写 _this.age = age * 将直接进入 _assertThisInitialized 方法,然后报错,没有调用 super 方法 */ _this.age = age; return _this; } return Child; })(Parent); var c = new Child(); // 如果不用 babel转码,直接在浏览器里运行,不写 super,结果如下和 babel 转义后报错信息不一样 // 原生报错信息: Uncaught ReferenceError: Must call super constructor in derived class before accessing "this" or returning from derived constructor
原生构造函数不可通过 extends 实现继承 ES6阮一峰
因为子类拿不到 原生父类内部的对象,即是通过 call 也不行
new 运算符上面说了继承,那产生实例的操作符 new 是什么原理?
var obj = {} obj.__proto__ = Child.prototype F.call(obj) // 1. 创建一个空对象 // 2. 将这个空对象的 __proto__ 属性 指向了构造函数的 prototype 属性 上 ==> 继承原型属性方法 // 3. 将构造函数的 this 指针替换成了 obj(实例),再调用 构造函数 ===> 继承实例属性方法与原型链有关的几个方法
hasOwnProperty : 该方法只会查找对象本身是否有某属性,不会去原型链上寻找
A.isPropertyOf(instanceA) : 判断 A 是不是 instanceA 的原型对象
instanceof : 判断对象是不是某个构造函数的实例
__proto__ 只是浏览器厂商的私有实现,规范并不支持,规范支持Object.getPrototypeOf 和 Object.setPrototypeOf
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/105935.html
摘要:今天闲来无事,看见几行小字。又说所有对象,继承终是。强行押韵一波这首诗的意思就是说的我今天没有什么事情,然后无意中又在网上看到了任何对象都是从对象继承而来的这句话。一时兴起,便去验证这句话。 今天闲来无事,看见几行小字。又说所有对象,继承终是Obj。—— 强行押韵一波 这首诗的意思就是说的我今天没有什么事情,然后无意中又在网上看到了任何对象都是从Object对象继承而来的这句话。一时兴...
摘要:在继承的构造函数中,我们必须如上面的例子那么调用一次方法,它表示构造函数的继承,与中利用继承构造函数是一样的功能。 showImg(https://segmentfault.com/img/remote/1460000009078532); 在实际开发中,ES6已经非常普及了。掌握ES6的知识变成了一种必须。尽管我们在使用时仍然需要经过babel编译。 ES6彻底改变了前端的编码风格,...
摘要:声明会提升,但是不会被初始化赋值,所以优先初始化赋值,则会进入暂时性死区,类似,变量内部启动严格模式的所有方法包括静态方法和示例方法都没有原型对象,所以也没有,不能使用来调用必须使用来调用内部无法重写类名 class声明会提升,但是不会被初始化赋值,所以优先初始化赋值,则会进入暂时性死区,类似let,const变量 const bar = new Bar(); // ok funct...
摘要:组合式继承是最常用的继承模式,但组合继承使用过程中会被调用两次一次是创建子类型的时候,另一次是在子类型构造函数的内部。 首先需要了解原型链机制: 原型链作为实现继承的主要方法,其基本思想就是利用原型让一个引用类型继承另 一个引用类型的属性和方法. 构造函数、原型、实例之间的关系: 每个构造函数都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constr...
摘要:对象扩展简洁表示法属性表达式值用中括号包起来,就是个表达式跟的功能是一样的数组也是引用类型,值虽然都是空,但指向不同的内存地址实现对象的拷贝浅拷贝只拷贝对象自身的属性,如果对象有继承,继承的属性不会被拷贝只拷贝可枚举属性,不可枚举属性不会被 对象扩展 简洁表示法 { let o = 1,k = 2; let es5 = { o: o, k...
阅读 2846·2023-04-25 19:08
阅读 1398·2021-11-16 11:45
阅读 1913·2021-10-13 09:40
阅读 3931·2021-09-30 09:47
阅读 2394·2019-08-30 15:44
阅读 2206·2019-08-30 13:03
阅读 1349·2019-08-30 12:56
阅读 1868·2019-08-26 14:04