课程标题 面向对象及原型链
课程目标
- 面向对象思想
- 原型及原型链
- 继承
知识点
- 面向对象思想
- 原型链的指向
- new关键字的步骤
new关键字做了什么?
- 创建一个新的对象,继承自构造函数的原型。
- 实例的proto指向构造函数的prototype。
- 将this指向新创建的对象。
- 返回这个新创建的对象。
a. 如果构造函数没有显示的返回值或者返回值是基本类型,返回this
b. 如果构造函数有显示的返回值且是对象类型,返回这个对象
function Player(name){
this.name = name;
}
function objectFactory(){
let obj = new Object();
let FunctionConstructor = [].shift.call(arguments);
obj.__proto__ =FunctionConstructor.prototype;
let resultObj = FunctionConstructor.apply(obj,arguments);
return typeof resultObj === 'object' ? resultObj : obj;
}
const p1 = objectFactory(Player,'df');
console.log(p1);
继承
原型链继承
function Parent(){
this.name = 'parentName';
}
Parent.prototype.getName = function ()
{
console.log(this.name)
}
function Child () {}
// const child = new Child();
// child.__proto__ === Child.prototype
// Child.prototype.__proto__ === Parent.prototype
Child.prototype = new Parent();
Child.prototype.constructor = Child
const child1 = new Child()
child1.getName()
隐含的问题
- 如果一个实例操作了引用类型的原型属性,所有实例都会受到影响。
- 创建Child实例的时候无法传参
构造函数继承
针对上述问题1, 想办法把Parent上的属性和方法,添加/复制到Child上而不是都存在原型对象/Parent上,防止被实例共享。
实现
function Parent() {
this.actions=['eat','sleep'];
this.name = 'parentName';
}
function Child() {
Parent.call(this)
// 相当于执行了Parent内部的两个语句
// this.actions = ['eat', 'sleep'];
// this.name = 'parentName';
}
const child1 = new Child()
const child2 = new Child()
child1.actions.pop();
console.log(child1.actions) // ['eat']
console.log(child2.actions) // ['eat', 'sleep']
解决第二个不能传参的问题
function Parent(name,actions) {
this.actions=actions;
this.name = name;
}
function Child(id,name,actions) {
Parent.call(this,name,actions)
this.id = id;
}
const child1 = new Child('c1','c1name',['eat'])
const child2 = new Child('c2','c2name',['play','jump','ball'])
console.log(child1.actions) // ['eat']
console.log(child2.actions) // ['play','jump','ball']
隐含的问题
- 属性或者方法被继承的话,只能在构造函数中定义。每次创建实例都会执行一遍构造函数,多占用内存。
组合集成
原型链继承,实现了基本的继承,方法存在prototype上,子类可以直接调用,但是引用类型的属性会被所有实例共享,并且不能传参。
构造函数继承,解决了原型链的两个问题,但是构造函数内重复创建方法,导致内存占用过多。
实现
function Parent(name,actions) {
this.actions=actions;
this.name = name;
}
Parent.prototype.eat = function (){
console.log('eat')
}
function Child (id) {
Parent.apply(this,Array.from(arguments).slice(1));
this.id = id;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const c1 = new Child(1, 'c1',['xxx'])
const c2 = new Child(2, 'c2', ['hhhh'])
c1.eat();
c2.eat();
隐含的问题
- 调用了两次Parent的构造函数,做了无意义的重复操作。
寄生组合式继承
为了解决上述问题
function Parent(name,actions) {
this.actions=actions;
this.name = name;
}
Parent.prototype.eat = function (){
console.log('eat')
}
function Child (id) {
Parent.apply(this,Array.from(arguments).slice(1));
this.id = id;
}
// Child.prototype = new Parent();
//let TempFunction = function () {};
//TempFunction.prototype = Parent.prototype;
//Child.prototype = new TempFunction();
Child.prototyp = Object.create(Parent.prototyp);
Child.prototype.constructor = Child;
const c1 = new Child(1, 'c1',['xxx'])
const c2 = new Child(2, 'c2', ['hhhh'])
c1.eat();
c2.eat();
class
class Parent {
constructor(){
this.name ='aaa';
}
getName() {
console.log('getName');
}
}
class Child extends Parent {
constructor() {
super() ;
}
}
const p1 = new Child ();
p1.getName()
课后问题
- new 做了什么? 如何实现
a. 基于构造函数的prototype创建了一个对象。
b. 将this指向这个对象。
c. 实例的proto指向构造函数的prototype。
c. 返回这个新创建的对象,如果构造函数没有返回或者返回了值类型,直接返回this,如果构造函数返回引用类型,返回这个对象。 - 4种继承方式的优缺点?如何实现?
a.原型链
b. 构造函数
c. 组合模式
d. 寄生组合模式 可用Objec.create()模拟 - 如何判断一个函数是否用了同一块内存地址?
用严格相等 === - 如何在原型链上去找一个属性?