Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS原型链与继承相关 #7

Open
axlroseart opened this issue Aug 14, 2019 · 6 comments
Open

JS原型链与继承相关 #7

axlroseart opened this issue Aug 14, 2019 · 6 comments

Comments

@axlroseart
Copy link
Owner

No description provided.

@axlroseart
Copy link
Owner Author

axlroseart commented Aug 14, 2019

简单回顾下构造函数,原型和实例的关系:

每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针.

组合继承:

基本思路: 使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.

const Father = function(name) {
 this.name = name
 this.height = 180
}
// 父类的原型方法
Father.prototype.sayName = function() {
 console.log(this.name)
}
// 子类
const Child = function(name) {
 Father.call(this, name)
}
Child.prototype = new Father()

Child的原型指向Father的构造函数:
image

// 子类实例1
const child1 = new Child('Gogo')
child1.sayName() // => Gogo
// 链式查找
child1
// 修改继承的属性
child1.height = 60

实例child1原型指向父类Father
image

// 子类实例2
const child2 = new Child('Coco')
child2.sayName() // => Coco
console.log(child2.height) // => 180

实例child1和实例child2都继承了Father的属性和方法且互不干扰,
但是这种实现方式使Father这个父类的构造函数被调用了两次。

Note:

使用构造函数可以避免原型上运用类型属性的共享;
call方法可以实现向父类传递参数;

原型继承

采用ES6的Object.create()方法

const person = {
 skinColor: ['black', 'white', 'yellow']
}
let gogo = Object.create(person)
gogo.skinColor.push('red')
let coco = Object.create(person)
coco.skinColor.push('blue')
console.log(person.skinColor) // => ["black", "white", "yellow", "red", "blue"]

image
可看出skinColor在gogo的原型属性上
Object.create的第二个参数与Object.defineProperties()方法的第二个参数格式相同:
每个属性都是通过自己的描述符定义的,以这种方式指定的任何属性都会覆盖原型对象上的同名属性。例如:

let aa = Object.create(person, { skinColor: { value: 'blackAndWhite' } })

image
提醒: 原型式继承中, 包含引用类型值的属性始终都会共享相应的值, 就像使用原型模式一样

new 的实现过程:

比如要实例化类F

const obj = {}
obj.__proto__ = F.prototype
F.call(obj)

属性查找

hasOwnProperty

判断属性是否为对象自身的属性,不会去原型上查找

instanceof 和 isPrototypeOf

isPrototypeOf 用来判断该方法所属的对象是否为参数的原型对象,比如:
console.log(Father.prototype.isPrototypeOf(child1))

instanceof 用来判断对象是否为构造器的一个实例

@axlroseart
Copy link
Owner Author

this 根本不受原型的影响

无论在哪里找到方法:在对象或者原型中。调用方法时,this 始终是点之前的对象。

因此,实际上 setter 使用 admin 作为 this,而不是 user。

这是一件非常重要的事情,因为我们可能有一个有很多方法而且继承它的大对象。然后我们可以在继承的对象上运行它的方法,它们将修改这些对象的状态,而不是大对象的。

例如,这里的 animal 代表“方法存储”,而且 rabbit 在使用它。

调用 rabbit.sleep(),在 rabbit 对象上设置 this.isSleeping:

// animal has methods
let animal = {
  walk() {
    if (!this.isSleeping) {
      alert(`I walk`);
    }
  },
  sleep() {
    this.isSleeping = true;
  }
};

let rabbit = {
  name: "White Rabbit",
  __proto__: animal
};

// modifies rabbit.isSleeping
rabbit.sleep();

alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (no such property in the prototype)

@axlroseart
Copy link
Owner Author

每个函数都有 "prototype" 属性,即使我们不设置它。

默认的 "prototype" 是一个只有属性 constructor 的对象,它指向函数本身。

像这样:

function Rabbit() {}

/* default prototype
Rabbit.prototype = { constructor: Rabbit };
*/

@axlroseart
Copy link
Owner Author

JavaScript 本身并不能确保正确的 "constructor" 函数值

是的,它存在于函数的默认 "prototype" 中,但仅此而已。之后会发生什么 —— 完全取决于我们自己。

特别是,如果我们将整个默认原型替换掉,那么其中就不会有构造函数。

例如:

function Rabbit() {}
Rabbit.prototype = {
  jumps: true
};

let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false

因此,为了确保正确的 "constructor",我们可以选择添加/删除属性到默认 "prototype" 而不是将其整个覆盖:

function Rabbit() {}
// Not overwrite Rabbit.prototype totally
// just add to it
Rabbit.prototype.jumps = true
// the default Rabbit.prototype.constructor is preserved

或者,也可以手动重新创建 constructor 属性:

Rabbit.prototype = {
  jumps: true,
  constructor: Rabbit
};

// 这样的 constructor 也是正确的,因为我们手动添加了它

@axlroseart
Copy link
Owner Author

axlroseart commented Jan 2, 2020

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

delete rabbit.eats;

alert( rabbit.eats ); // true

所有 delete 操作都直接应用于对象。这里 delete rabbit.eats 试图从 rabbit 中删除 eats 属性,但 rabbit 对象并没有 eats 属性。所以这个操作不会有任何 副作用。

@axlroseart
Copy link
Owner Author

axlroseart commented Jan 2, 2020

let user2 = new Object('Pete')
内置的 Object 构造函数忽略参数,它总是创建一个空对象 —— that's what we got from user2。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant