-
原型和原型链
-
1.理解原型设计模式以及JavaScript中的原型规则
-
原型设计模式
-
概念:是指原型实例指向创建对象的种类,并通过拷贝这些原型创建新的对象,是一种用来创建对象的模式,也就是创建一个对象作为另一个对象的prototype属性。
-
实现原型模式
-
方法一:使用 Object.create(prototype, optionalDescriptorObjects)
var vehiclePrototype = { model:"保时捷", getModel: function () { console.log(‘车辆模具是:‘ + this.model); } }; var vehicle = Object.create(vehiclePrototype,{ "model":{ value:"法拉利" } }); vehicle.getModel();
-
方法二:使用 prototype
var vehiclePrototype = { init: function (carModel) { this.model = carModel || "保时捷"; }, getModel: function () { console.log(‘车辆模具是:‘ + this.model); } }; function vehicle(model) { function F() { }; F.prototype = vehiclePrototype; var f = new F(); f.init(model); return f; } var car = vehicle(‘法拉利‘); car.getModel();
-
-
原型规则
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可*扩展属性;
var arr = []; arr.a = 1;
-
所有的引用类型(数组、对象、函数),都有一个_proto_属性(隐式原型),属性值是一个普通的对象;
-
所有的函数,都具有一个prototype(显式原型),属性值也是一个普通对象;
-
所有的引用类型(数组、对象、函数),其隐式原型指向其构造函数的显式原型;(obj.proto === Object.prototype);
-
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中去寻找;
-
-
-
2.instanceof的底层实现原理,手动实现一个instanceof
-
instanceof的实现实际上是调用JS的内部函数
[[HasInstance]]
来实现的 -
实现原理:只要右边变量的
prototype
在左边变量的原型链上即可。因此,instanceof
在查找的过程中会遍历左边变量的原型链,直到找到右边变量的prototype
,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。 -
var arr = {}; console.log(new_instance_of(arr,Object)); function new_instance_of(leftValue, rightValue){ let rightProto = rightValue.prototype; leftValue = leftValue.__proto__; while(true){ if(leftValue === null){ return false; } if(leftValue === rightProto){ return true; } leftValue = leftValue.__proto__; } }
-
-
4.实现继承的几种方式以及他们的优缺点
https://www.cnblogs.com/humin/p/4556820.html
-
原型链继承(推荐指数:★★)
- 核心:将父类的实例作为子类的原型
- 优点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
- 缺点:
- 要想为子类新增属性和方法,必须要在
new Animal()
这样的语句之后执行,不能放到构造器中 - 无法实现多继承
- 来自原型对象的所有属性被所有实例共享
- 创建子类实例时,无法向父类构造函数传参
- 要想为子类新增属性和方法,必须要在
-
构造继承(推荐指数:★★)
- 核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
- 优点:
- 解决了1中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)
- 缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
-
实例继承(推荐指数:★★)
- 核心:为父类实例添加新特性,作为子类实例返回
- 优点:不限制调用方式,不管是
new 子类()
还是子类()
,返回的对象具有相同的效果 - 缺点:
- 实例是父类的实例,不是子类的实例
- 不支持多继承
-
拷贝继承(推荐指数:★)
- 优点:支持多继承
- 缺点:
- 效率较低,内存占用高(因为要拷贝父类的属性)
- 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
-
组合继承(推荐指数:★★★★)
- 核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
- 优点:
- 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
- 缺点:调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
-
寄生组合继承(推荐指数:★★★★)
- 核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
- 优点:堪称完美
- 缺点:实现较为复杂
-
-
5.至少说出一种开源项目(如Node)中应用原型继承的案例
-
6.可以描述new一个对象的详细过程,手动实现一个new操作符
// ES5构造函数 let Parent = function (name, age) { //1.创建一个新对象,赋予this,这一步是隐性的 // let this = {}; //2.给this指向的对象赋予构造属性 this.name = name; this.age = age; //3.如果没有手动返回对象,则默认返回this指向的这个对象,也是隐性的 // return this; }; const child = new Parent(); winter重学前端专栏中原理的描述: ? 以构造器的prototype属性为原型,创建新对象; ? 将this(也就是上一句中的新对象)和调用参数传给构造器,执行; ? 如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象。 简单来说就是new过程中会新建对象,此对象会继承构造器的原型与原型上的属性,最后它会被作为实例返回这样一个过程。
// 构造器函数 let Parent = function (name, age) { this.name = name; this.age = age; }; Parent.prototype.sayName = function () { console.log(this.name); }; //自己定义的new方法 let newMethod = function (Parent, ...rest) { // 1.以构造器的prototype属性为原型,创建新对象; let child = Object.create(Parent.prototype); // 2.将this和调用参数传给构造器执行 let result = Parent.apply(child, rest); // 3.如果构造器没有手动返回对象,则返回第一步的对象 return typeof result === ‘object‘ ? result : child; }; //创建实例,将构造函数Parent与形参作为参数传入 const child = newMethod(Parent, ‘echo‘, 26); child.sayName() //‘echo‘; //最后检验,与使用new的效果相同 child instanceof Parent//true child.hasOwnProperty(‘name‘)//true child.hasOwnProperty(‘age‘)//true child.hasOwnProperty(‘sayName‘)//false
-
7.理解es6 class构造以及继承的底层实现原理
-
理解?
-
原理?
-
类的创建
//定义类 class Person{ // 类的静态方法,相当于Person.test = function(){console.log("类的静态方法");} static test() { console.log("类的静态方法"); } //constructor构造函数 constructor(name,age){ console.log("调用构造函数"); this.name = name; this.age = age; } //类的一般方法,定义在实例对象的原型对象上,相当于Person.prototype.show = function(){console.log("this.name,this.age");} show(){ console.log(this.name,this.age); } } let person1 = new Person("wzh",25); console.log(person1); person1.show(); Person.test();
-
类的继承
class Child extends Person{ constructor(name,age,sex){ super(name,age); //调用父类构造函数构造子类 this.sex = sex; } //重写父类同名函数 show(){ console.log(this.name,this.age,this.sex); } } let child = new Child("wzl",24,"男"); child.show();
-
-
本文作者:AlubNoBug