原生JS -- 带你深入理解‘JS实现继承‘的8种方式

1、概念

让某一个不具备方法和属性的对象,具有另一个对象的方法和属性,这个过程叫做继承

tips: 下面每种继承方式的总结很重要,注意阅读

2、继承方式

2.1、改变this指向

2.1.1 利用对象改变this指向的继承
var obj = {
	name: "我是obj",
	show: function() {
		console.log(this.name);
	}
}
obj.show(); // 我是obj
var obj2 = {
	name: "我是obj2"
}
// 想要利用show打印obj2的name
// obj2.show(); // 直接执行报错,show不属于obj2
obj.show.call(obj2); // 我是obj2,执行时让show方法的this指向强行改成obj2
2.1.2 构造函数改变this的指向
function Parent() {
	this.name = "假设我是父级";
}
Parent.prototype.init = function() {
 	console.log("hello");
}
function Child() {
	// 执行Parent()的this指向window
	// 需要将parent的this指向改成指向child将来的实例
	// 而这里chilid的this的指向就是将来的实例
	// 将parent的this改成child的this
	Parent.call(this);
    }
// 想让child获得parent的属性
var c = new Child();
console.log(c); // Child {name: "假设我是父级"}
console.log(c.name); // 假设我是父级
// c.init(); 报错,不能继承原型上的内容
  • 如果想继承多个父级
function P1() {
	// 每个父级的属性名不能重复,否则会覆盖
    this.cont = "属性1"
}
function P2() {
    // 每个父级的属性名不能重复,否则会覆盖
    this.cont1 = "属性2"
}
function P3() {
    this.cont2 = "属性3"
}
function C() {
	// 在子级内部将所有父级的this指向都改成指向自己的实例
    P1.call(this);
    P2.call(this);
    P3.call(this);
}
var cc = new C();
console.log(cc); // C {cont: "属性1", cont1: "属性2", cont2: "属性3"} 继承了多个父级的属性
  • 改变this指向继承的特点
    1.简单方便易操作
    2.可以实现多继承(子继承多个父)
    3.只能继承构造函数中的属性,不能继承原型上的方法

2.2、原型继承

2.2.1 原型继承
function Parent() {
    this.name = "构造函数里的内容";
}
Parent.prototype.init = function() {
    console.log("hello");
}
function Child() {}
// 原型继承的方式一:直接复制版原型继承
// 直接将父级的原型复制给子级的原型
// 不推荐,相当于浅拷贝,改变子级会改变父级
Child.prototype = Parent.prototype;
// 浅拷贝之后,再给子级更改方法,会同时改变父级的方法
Child.prototype.init = function() {
    console.log("world");
}
// 原型继承的方式二:利用是深拷贝复制父级,改变子级不会影响父级
for (var i in Parent.prototype) {
    Child.prototype[i] = Parent.prototype[i]
}
// 深拷贝之后,再给子级更改方法,不会改变父级的方法
Child.prototype.init = function() {
    console.log("world"); // world
}
var p = new Parent();
p.init();
console.log(p); // Parent {name: "构造函数里的内容"}

var c = new Child();
c.init();
console.log(c); // Child {},没有继承父级构造函数内部的内容
  • 原型继承–原型继承的特点
    可以继承原型上的方法,但是继承不了构造函数内的属性
2.2.2 原型链继承
// 构造一个父级函数
function Parent() {
    this.name = "父级里的内容";
}
// 给父级的prototype添加方法
Parent.prototype.init = function() {
    console.log("hello");
} 
// 构造一个子级函数
function Child() {}
// 原型链继承方式:把父级的实例赋值给子级的prototype
// 也就是说:子级的原型就是父级的实例,父级实例有的东西就能被子级的原型继承到
Child.prototype = new Parent();

// 给子级的prototype添加方法相当于给父级的实例添加方法
// 最终执行c.init()打印子级的内容,因为父级的实例在子级实例的上一层,读取,使用最近原则
// 不会影响到父级的方法,父级的方法在父级实例的原型里即子级实例的上上一层
// 如果想要改掉父级的方法:Child.prototype.__proto__.init = function(){...}
Child.prototype.init = function() {
     console.log("子级的内容");
}
var p = new Parent();
p.init(); // hello
console.log(p); // Parent {name: "父级里的内容"}

var c = new Child();
c.init(); // hello
console.log(c); // Child {} 在浏览器的结构如下👇👇👇

	Child {} // 子级的实例没有方法,也没有属性
    	__proto__: Parent // 上一层:子级的原型是,父级的实例
        	name: "父级里的内容" // 父级的实例里有属性
        	__proto__: // 上上一层:父级的原型有,方法
            	init: ƒ () // 方法在父级的原型里
            	constructor: ƒ Parent()
            	__proto__: Object
  • 原型继承–原型链继承特点
    1、可以继承原型上的所有内容(继承方法),也能继承构造函数内的内容(继承属性)
    2、不利于传参

2.3、混合继承

// 混合继承是将'改变this指向继承'和'原型继承'相结合
function Parent(parent) {
    // 父级接收参
    this.name = parent;
}
Parent.prototype.init = function() {
    console.log(this.name);
}

// 子级接收参数
function Child(son) {
    // 改变this指向实现继承属性
    // 接收参数后再把参数给this的第二个参数,覆盖原函数的参数
    Parent.call(this, son);
}
// 原型继承:只能拿到方法,不能拿到属性
for (var i in Parent.prototype) {
    Child.prototype[i] = Parent.prototype[i];
}
// 父级传参在new执行函数时传
var p = new Parent("父级的内容");
p.init(); // 父级的内容
console.log(p); // Parent {name: "父级的内容"}

// 子级传参在new执行函数时传
var c = new Child("子级的内容");
c.init(); // 子级的内容
console.log(c); // Child {name: "子级的内容"}
  • 混合继承也能实现继承多个父级
function Another() {
    // 注意属性名不能重名,否则会覆盖
    this.xixi = "又一个父级";
}
// 注意方法名也不能重名,否则会覆盖
Another.prototype.show = function() {
    console.log(this.xixi);
}
// 再执行深拷贝
for (var i in Another.prototype) {
    Child.prototype[i] = Another.prototype[i];
}
// 注意其他父级都要在子级内执行并改变this指向
function Child() {
    // 改变this指向实现继承属性
    Another.call(this);
}
  • 混合继承的特点
    1、可以继承构造函数,可以继承原型
    2、可以实现多继承

2.4、class继承

  • 其实就是面对对象编程的class语法
  • 语法层面上的继承
  • ES6对混合继承的封装
class Parent {
    constructor(p) {
        this.str = p;
    }
    init() {
        console.log(this.str); // 父级的内容
    }
}
// 创建子级构造函数时,使用extends,表示要继承父级了
class Child extends Parent {
    // 子级的参数必须要在constructor接收,且
    constructor(c) {
        // super不写会报错
        // super也要传参
        super(c);
    }
    // 如果给子级添加方法,不会影响父级的方法
    // 具体参考原型链继承
    init() {
        console.log("子级的方法"); // 子级的方法
    }
}
var p = new Parent("父级的内容");
p.init();
console.log(p); // Parent {str: "父级的内容"}
var c = new Child("子级的内容");
c.init();
console.log(c); // Child {str: "子级的内容"}

class继承的特点:也就是混合继承的特点

2.5、对象的继承:Object.create()

  • 根据指定对象创建新对象:Object.create(指定对象)
var obj = {
    name: "admin"
}
var obj2 = Object.create(obj);
console.log(obj == obj2); // false
console.log(obj); // {name: "admin"}
console.log(obj2); // {}
console.log(obj.name); // admin
console.log(obj2.name); // admin,继承到的属性在obj2的原型上

2.6、数组的继承:Array.form()

  • 根据指定数组创建新数组:Object.create(指定数组)
var arr = [1, 2, 3];
var arr2 = Array.from(arr);
console.log(arr == arr2); // false
console.log(arr2); // [1, 2, 3]
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值