欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

JS继承

发布时间:2024/8/26 38 生活家
生活随笔 收集整理的这篇文章主要介绍了 JS继承 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

1. 原型链继承

它是下面这个形式的。

function F() {
  this.f_age = 8;
  this.f_name = 'father';
}
F.prototype.getFAge = function() {
  return this.f_age;
}
function S() {
  this.s_age = 3;
}
S.prototype = new F();
S.prototype.getSAge = function() {
  return this.s_age;
}
let s = new S();
console.log(s.getFAge()); // 8
console.log(s.getSAge()); // 3
console.log(s.constructor);  // [Function: F]
console.log(s instanceof S); // true
console.log(s instanceof F); // true

它的主要过程:

实例化父类 F -> f
将子类的原型对象 S.prototype 指向 f
实例化子类 S -> s

加深理解:
s.__proto__ -> S.prototype -> f.__proto__ -> F.prototype

需要注意的是:

本来 S 的 constructor 是 S,但是因为 S.prototype 重写了,所以现在 constructor 是 F.
查找机制: s -> S.prototype -> F实例 -> F.prototype

function F() {
  this.name = 'F';
}
F.prototype.name = 'F-prototype';
function S() {}
S.prototype = new F();
let s = new S();
console.log(s.name); // F

原型继承中 子类 重写 父类的方法

function F() {
  this.x = true;
}
F.prototype.getF = function() {
  return this.x;
}
function S() {
  this.y = false;
}
S.prototype = new F();
S.prototype.getS = function() {
  return this.y;
}
// 重写父类型中的 getF
S.prototype.getF = function() {
  return '33';
}
let s = new S();
console.log(s.getS()); // false 
console.log(s.getF()); // 33,   原来的 父类中 返回 true
delete(S.prototype.getF);
console.log(s.getF()); // true
如果你想要改变(覆盖)父类的方法,必须在子类的 原型对象 被 父类的实例 赋值之后 覆盖。后来居上,你懂的。
后来居上的 字面量方法 也一样,如下
function F() {}
function S() {}
S.prototype = new F();
// 下面的这句话重写了上面这句话。
S.prototype = {
  getX: function() {
    return '33';
  }
}

原型链继承的特点和示例代码:

a. 原来的父类实例属性 变为了 子类的原型属性。共享性。(也可以是缺点)

function F() {
  this.colors = ['red', 'blue'];
}
function S() {}
S.prototype = new F();
let s1 = new S();
let s2 = new S();
s1.colors.push('yellow');
console.log(s1.colors); // ['red', 'blue', 'yellow']
console.log(s2.colors); // ['red', 'blue', 'yellow'] 

b. 创建子类型的实例的时候,不能像超类型的构造函数传递参数。(是不足之处)


2. 借用构造函数 继承

真是骚操作啊。

原理:

A.谁调用了函数,函数中的 this 就指向谁。
B.利用 apply 和 call 在 子类 内部调用 函数。

function F() {
  this.colors = ['red', 'blue'];
}
function S() {
  F.call(this);
}
let s1 = new S();
let s2 = new S();
s1.colors.push('yellow');
console.log(s1.colors);  // ['red', 'blue', 'yellow']
console.log(s2.colors);  // ['red', 'blue']

特点:

属性不会共享。(上面说了)
可以传参

function F(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}
function S(name) {
  F.call(this, name);
}
let s1 = new S('ccc');

缺点在于函数复用。

其实和 JS构造模式 是一个道理。在原型链 中的 共享的函数还是很有必要的。一些属性也是应该共享的。


3. 组合继承: 跟JS模式中的 组合构造模式 很像。

可以传参。
可以 选择 是否共享属性和方法。(非战争的年代,人们有权选择过自己的生活)

function F(name) {
  this.name = name;
  this.colors = ['red'];
}
F.prototype.getF = function() {
  console.log(this.name);
}
function S(name, age) {
  F.call(this, name);
  this.age = age;
}
S.prototype = new F();
S.prototype.constructor = S;
S.prototype.getS = function() {
  console.log(this.age);
}
// 强行让 constructor 为子类,缺点是 constructor 变为可枚举。
let s1 = new S('s1', 18);
let s2 = new S('s2', 20);
s1.colors.push('yellow');
console.log(s1);    // S {name: 's1', colors: ['red', 'yellow'], age: 18}
console.log(s2);    // S {name: 's2', colors: ['red'], age: 20}
delete(s1.colors);      // 删掉了 子类实例中,借用父类构造函数继承的 colors
console.log(s1.colors); // 子类实例中:父类原型中的 colors 还是存在的。
// ---------------------------------------------------------------
console.log(s1.constructor); // [Function: S]
console.log(Object.keys(S.prototype)); // 其中包含 constructor 属性

4. 原型式继承: 可以说是 ES3中对 ES5中 Object.create 的实现了。

缺点很明显:共享属性。

function Create(o) {
  function F(){};
  F.prototype = o;
  return new F();
}
let person = {
  name: '123',
  colors: ['red']
}
let f1 = Create(person);
let f2 = Create(person);
f1.colors.push('yellow');
console.log(f2.colors);   // ['red', 'yellow']
ES5中的 Object.create(用作新对象原型的对象,可选的定义额外属性的对象)

若是没有第二个参数,和我们上方自己写的 Create 方法相同。

let person = {
  name: '123',
  colors: ['red']
}
let f1 = Object.create(person);
let f2 = Object.create(person);
f1.colors.push('yellow');
console.log(f2.colors);   // ['red', 'yellow']

若是写了第二个参数,则会覆盖掉原型中的 属性。

let person = {
  name: '123',
  colors: ['red']
}
let f1 = Object.create(person, {
  colors: {
    value: ['red']
  }
});
let f2 = Object.create(person, {
  colors: {
    value: ['red']
  }
});
f1.colors.push('yellow');
console.log(f2.colors);   // ['red']

在没有必要兴师动众地 创建构造函数,只是想让 一个对象 与 另外一个对象保持 相似的情况下,使用 原型式继承 就可以了。


5. 寄生组合式继承。

产生的原因是组合继承 的缺点:

调用两次父类。

1.创建子类原型的时候
2.子类型构造函数内部的调用

最后造成的结果。子类会包含 父类的全部 实例属性。并且在调用子类构造函数的时候会重写(覆盖)一些属性。

function Create(o) {
  function F() {};
  F.prototype = o;
  return new F();
}
// 1. 这一步为了 子类继承 父类的原型
function Inherit(subType, superType) {
  let prototype = Create(superType.prototype);
  // 只承包了 父类的原型。
  prototype.constructor = subType;
  // 增强对象,重写 被重写的 constructor
  subType.prototype = prototype;
}

function F(name) {
  this.name = name;
  this.colors = ['blue'];
}
F.prototype.say = function() {
  console.log(this.name);
}
// 2. 这一步为了 子类继承 父类的一些构造函数内部的东西
function S(name, age) {
  F.call(this, name);
  this.age = age;
}
Inherit(S, F);

疑问:其实本质上没有啥差别。主要的骚操作就要在于:子类的原型继承的地方改了下。


complete.

总结

以上是生活随笔为你收集整理的JS继承的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。