Appearance
JavaScript教程 - 9 面向对象
9.5 原型简介
讲解原型之前,先看一个栗子。
js
class Person {
name = 'Doubi';
sayHello() {
console.log('Hello');
}
sleep = function() {
console.log('sleep');
}
}
let person = new Person();
person.age = 13; // 添加属性
person.eat = function() { // 添加方法
console.log('eat');
}
console.log(person); // Person {name: 'Doubi', age: 13, sleep: ƒ, eat: ƒ}- 在上面的代码中,通过 Person 类创建了 person 对象,并给 person 对象添加了属性和方法。
但是在打印 person 对象的时候,发现并没有 sayHello() 方法,这是为什么呢?
因为对象中存储属性的区域实际有两个:
对象自身
在类中通过赋值的方式(
x = value)添加的属性,位于对象自身中;直接通过
对象.属性 = value的方式添加的属性,位于对象自身中。原型对象
对象中还有一些内容,会存储在别的对象中,也就是原型对象,也就是说原型对象中也会存储对象的属性和方法,当我们通过对象来访问属性或方法的时候,会先在对象自身中寻找,如果找不到,就会去原型对象中寻找。
所以上面通过
sayHello(){}添加的方法,实际是添加到原型对象中了。
在对象中,会有一个属性存储原型对象的引用,这个属性叫 __proto__ :

我们可以通过如下方式访问原型对象:
js
let person = new Person();
console.log(person.__proto__);
console.log(Object.getPrototypeOf(person));- 可以通过
对象.__proto__获取到原型对象,但是千万不要通过对象.__proto__ = value来修改原型对象,会导致问题; - 还可以通过
Object.getPrototypeOf(obj)的方式来获取原型对象。
JavaScript中为什么需要原型呢?
- 将方法定义在原型上,所有对象可以共享同一份方法,而不是每个实例都创建独立的副本,这样可以节省内存。
所以同一个类的多个对象的原型对象指向的是同一个对象,举个栗子:
js
class Person {
constructor(name) {
this.name = name;
}
}
let p1 = new Person('Doubi');
let p2 = new Person('Niubi');
console.log(p1.__proto__ === p2.__proto__) // true- JavsScript的继承是使用原型实现的,这个下面细讲。
9.6 原型链与继承
1 构造函数
在前面我们讲创建对象的方式,有以下三种方式:
js
// --方式1
let person1 = {
name: "Doubi",
age: 13,
};
// --方式2
let person2 = new Object();
person2.name = "Doubi";
person2.age = 13;
// --方式3
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
let person3 = new Person("Doubi", 13);在 ES6 之前,创建对象是通过构造函数来创建的,class 是 ES6 中引入的,class 其实是构造函数的语法糖(更简洁更易读的语法),而 class 实现的继承底层仍是原型,新的 class 写法只是让原型的写法更加的清晰、更像面向对象编程的语法而已。
所以既然 class 是语法糖,那就先从构造函数讲起。
创建一个 Person 的构造函数,用来创建 Person 对象:
js
// 函数
function Person() {
}
let person = new Person(); // 通过 new 使用
console.log(person); // Person {}- 上面的
Person()函数就是一个构造函数,构造函数和普通的函数没有本质区别,只是调用方式不同。当我们使用new来调用一个函数,那么它就是被当做构造函数,返回的就是一个对象。
当然我们想把一个函数作为构造函数来创建对象,那么会在对象中添加属性、方法,是我们使用函数的方式不同,才区分了构造函数和普通函数:
js
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
let person = new Person('Doubi', 13);
console.log(person); // Person {name: 'Doubi', age: 13}使用构造函数创建的实例,都有一个 constructor 属性指向构造函数:
js
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
let p1 = new Person("Doubi", 13);
let p2 = new Person("Niubi", 18);
console.log(p1.constructor === Person); // true
console.log(p2.constructor === Person); // true
console.log(p1.constructor === p2.constructor); // trueclass 是构造函数的语法糖,那么使用类也是一样的:
js
// 构造函数
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
let p1 = new Person("Doubi", 13);
let p2 = new Person("Niubi", 18);
console.log(p1.constructor === Person); // true
console.log(p2.constructor === Person); // true
console.log(p1.constructor === p2.constructor); // true
console.log(p1.hasOwnProperty("constructor")); // false但是上面 person1.hasOwnProperty("constructor") 判断对象是否有 constructor 返回的是 false,也就是说实例本身是没有 constructor 属性的,这个 constructor 属性是继承自实例的原型对象。
也就是:p1.constructor -> p1.__proto__.constructor -> Person 。
下面来仔细说一下原型链。
内容未完......




