原型和原型链

什么是原型?

我们创建的每一个构造函数都有一个 prototype 属性,它指向一个对象,这个对象可以包含由该函数创建的所有实例共享 属性和方法。

prototype就是调用构造函数创建的实例原型对象

使用原型对象的好处?

可以让所有的对象实例共享它所包含的属性和方法

构造函数、实例、原型对象的关系?

每个构造函数都有一个原型对象。也就是prototype

每个原型对象都有一个指向构造函数的属性,就是constructor

每个实例都有一个__proto__属性,指向原型对象。

__proto__被称为隐式原型,prototype被称为显式原型,也就是说一个对象的隐式原型指向了该对象的构造函数的显式原型。在显式原型上

定义的属性和方法,通过隐式原型传递给了构造函数的实例,这样实例就能访问到构造函数原型上的属性和方法了。

什么是原型链?

prototype机制就是存在于对象中的一个内部链接,它会引用其他对象。

如果在当前对象没有找到想要的属性或方法时,js 引擎就会沿着 prototype 关联的对象继续查找,以此类推。这一系列对象的链接就被称为原型链。

原型链最顶端是 Object.prototype , 它的__proto__仍然存在,为 null。

构造函数

构造函数里可以有实例成员静态成员

  • 实例成员:就是在构造函数内部,通过this添加的成员。实例成员可以通过实例对象来访问。

  • 静态成员:在构造函数本身上添加的成员,只能通过构造函数来访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Star(name, age) {
//实例成员
this.name = name;
this.age = age;
}
//静态成员
Star.sex = "女";

let stars = new Star("小红", 18);
console.log(stars); // Star {name: "小红", age: 18}
console.log(stars.sex); // undefined 实例无法访问sex属性

console.log(Star.name); //Star 通过构造函数无法直接访问实例成员
console.log(Star.sex); //女 通过构造函数可直接访问静态成员

问题扩展

通过构造函数创建的每个实例的方法是共享的吗?

不一定,分两种情况讨论:

  1. 在构造函数上直接定义的方法不共享。
1
2
3
4
5
6
7
8
9
10
11
function Star() {
this.sing = function () {
console.log("我爱唱歌");
};
this.name = "leemo";
}
let stu1 = new Star();
let stu2 = new Star();
stu1.sing(); //我爱唱歌
stu2.sing(); //我爱唱歌
console.log(stu1.sing === stu2.sing); // false

每次生成实例都会新开辟一块内存存储属性和方法。

如果属性是基本类型,就不存在共享的问题,具体是否相同要看具体值的内容是否相同。

  1. 通过原型添加的方法是共享的。
1
2
3
4
5
6
7
8
9
10
11
function Star(name) {
this.name = name;
}
Star.prototype.sing = function () {
console.log("我爱唱歌", this.name);
};
let stu1 = new Star("小红");
let stu2 = new Star("小蓝");
stu1.sing(); //我爱唱歌 小红
stu2.sing(); //我爱唱歌 小蓝
console.log(stu1.sing === stu2.sing); //true

综上得出定义构造函数的规则。: 公共属性定义到构造函数里面,公共方法定义到原型对象上。

Object 和 Function

1
Function.prototype.__proto__ === Object.prototype;

所有函数的默认原型都是Object实例。

1
2
3
4
5
6
7
8
9
10
11
function fn() {}

fn.__proto__ === Function.prototype; //true
fn instanceof Function; //true
fn instanceof Object; //true

Function.__proto__ === Function.prototype; //true
Object.__proto__ === Function.prototype; //true

fn.prototype.__proto__ === Object.prototype; //true
fn.prototype.constructor === fn; //true

总结:
函数即是函数又是对象,含有__proto__prototype 属性。

__proto__指向Function.prototypeFunction.prototype__proto__属性又指向了 Object.prototype
prototype最终指向Object.prototype

如何获得对象非原型链上的属性?

使用hasOwnproperty方法来判断属性是否属于原型链的属性。