二,构造函数和原型
1,创建对象的三种方式
在es6之前,是没有class类这个概念的,创建类都是用构造函数创建的
创建对象的三种方式
-
对象字面量
var obj = {};
-
new Object()
var obj = new Object()
-
自定义构造函数
function People() { this.name = name; this.age = age; this.sing = function() {} }
创建实例分为四步:
- 开辟一个新空间
- 执行构造函数里面的代码
- 把this里面的属性都赋值给这个空间
- 返回这个空间给new的哪个变量
2,静态成员和实例成员
通过this添加的属性叫做实例,通过给函数本身添加的属性教静态成员
-
实例成员
function People() { this.name = name; // 实例成员 this.age = age; // 实例成员 } var a = People() console.log(a.name) // 实例成员只能通过访问属性的方式访问
-
静态成员
function People(name, age) { this.name = name; this.age = age; } People.other = '静态成员'; console.log(People.other); // 静态成员只能通过构造函数的方式来访问
-
构造函数的问题
构造函数很好用,但是会造成内存浪费的问题
每个通过构造函数创建的实例都会有性能浪费的问题,实例里面的方法存放的地址是不一样的!!!!
function People(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.log(this.name + '说话了');
}
}
class People1 {
constructor(name, age) {
this.name = name;
this.age = age;
}
say() {
console.log(this.name + '说话了');
}
}
var a = new People('a', 20);
var b = new People('b', 21);
console.log('构造函数中: ' + (a.say === b.say)); // 在构造函数中方法是存不同地方的
var a1 = new People1('a1', 20);
var b1 = new People1('b1', 21);
console.log('类中: ' + (a1.say === b1.say)); // 在类中方法是存在一个地方的
3,构造函数原型对象:prototype
-
构造函数通过原型分配的函数是所有对象
共享的
-
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,之一这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被够着函数所有(本质上解决了不同实例的函数指向,解决了内存浪费)
function People(name, age) { this.name = name; this.age = age; this.sing = function() {} } var a = new People('xyb', 20); var b = new People('lyw', 19); console.log(a.sing === b.sing); // 两个实例的方法不是同一个! People.prototype.say = function() {} console.log(a.say === b.say); // 两个实例的方法是同一个!
-
原型是什么?
一个对象,我们也称 prototype 原型对象
-
原型的作用是什么?
共享方法,解决构造函数实例出来的对象方法性能浪费问题
- 一般情况下,我们公共属性定义到构造函数里面,公共方法定义在原型对象身上
4,实例对象原型:__ proto __
实例都会有一个属性 __ proto__ 指向构造函数的原型,之所以实例对象可以使用构造函数原型中的函数,就是因为有 __ proto__ 这个属性的指向
实例对象的__proto__
和构造函数的 prototype
是等价的
function People(name, age) {
this.name = name;
this.age = age;
}
People.prototype.sing = function() {
console.log(this.name + '说话了');
}
var a = new People('xyb', 20);
console.log('a.__proto__:' + a.__proto__);
console.log('People.prototype:' + People.prototype);
console.log(a.__proto__ === People.prototype);
- 方法的查找顺序:
- 如果构造函数创造函数的时候有方法,就使用构造函数里的
- 如果没有,则在对象的__ proto __ 属性中查找,也就是在构造函数的原型中查找
5,constructor 构造函数
对象原型(__ proto __) 和 构造函数(prototype)原型对象里面都有一个 constructor 属性,它指回构造函数
-
作用:用来指明是引用了哪个构造函数,它可以让原型对象指挥原来的构造函数
-
因为构造函数的原型和实例的原型是同一个对象,所以他们原型的 constructor 是相同的
console.log(a.__proto__.constructor === People.prototype.constructor); // true
-
很多情况下,我们需要手动的利用 constructor 这个属性指回原来的构造函数
function People(name, age) { this.name = name; this.age = age; } People.prototype = { // 这里我们是重新把构造函数的原型对象重新赋值了,所以需要重新手动添加初始化函数 constructor: People, sing: function() { console.log(this.name + '唱歌了'); }, say: function() { console.log(this.name + '说话了'); } } var a = new People('xyb', 20); console.log(a.__proto__); console.log(People.prototype); console.log(a.__proto__.constructor); console.log(People.prototype.constructor);
6,构造函数丶实例丶原型对象三者之间的关系
7,JavaScript 的成员查找机制
-
当访问一个对象的属性(包括方法),首先看看,构造函数有没有创建这些方法或属性
-
如果找不到,就去这个对象的原型(也就是__ proto __和prototype指向的原型对象中)里面找
-
如果没有找到就一直往上找,也就是原型对象的原型里面找(object的原型对象)
-
以此类推,直到查到Object原型对象为null时停止
function People(name, age) { this.name = name; this.age = age; } People.prototype = { // 这里我们是重新把构造函数的原型对象重新赋值了,所以需要重新手动添加初始化函数 constructor: People, sing: function() { console.log(this.name + '唱歌了'); }, say: function() { console.log(this.name + '说话了'); } } Object.prototype.sex = 'obj的sex' People.prototype.sex = 'peo的sex' var a = new People('xyb', 20); console.log(a.sex); // a本身没有sex属性,去他的原型里面找,原型没有就去原型的原型里面找
8,原型对象this指向
function People(name, age) {
this.name = name;
this.age = age;
console.log('构造函数中的this:', this);
}
People.prototype.say = function() {
console.log('原型对象中的this:', this); // 谁调用了这个say方法,this就指向谁
}
var a = new People('xyb', 20); // People {name: "xyb", age: 20}
var b = new People('lyw', 19); // People {name: "lyw", age: 19}
// 1. 构造函数中的this指向的是对象实例
a.say(); // People {name: "xyb", age: 20}
b.say(); // People {name: "lyw", age: 19}
案例2:利用原型扩展内置对象方法
通过原型对象,对原来的内置对象进行扩展自定义方法,比如给数组增加求和方法
Array.prototype.sum = function() {
var total = 0;
for (var i = 0; i < this.length; i++) {
total += this[i];
}
return total
}
var arr = new Array(10, 20, 30);
console.log(arr.sum()); // 60
// ----------错误示例----------这里直接把Array的原型对象覆盖掉了
Array.prototype ={
sum: function() {
var total = 0;
for (var i = 0; i < this.length; i++) {
total += this[i];
}
return total
}
}