构造函数 原型对象prototype 实例对象原型__proto__ 原型链 继承
构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总是和new一起使用,可以把对象中一些公共的部分抽取出来,然后封装到这个函数里面;
new 在执行时做的事情
- 在内存中创建一个新的空对象;
- 让 this 指向这个空对象;
- 执行构造函数里面的代码,给这个新对象添加属性和方法;
- 返回这个新对象(所以构造函数里面不需要 return)
静态成员和实例成员
JavaScript 的构造函数中可以添加一些成员,可以再构造函数本身上添加,也可以在构造函数内部的 this 上添加,通过这两种方式添加的成员,分别称为 静态成员 和 实例成员
- 静态成员:在构造函数本身上添加的成员称为静态成员,只能由构造函数本身访问;
- 实例成员:在构造函数内部创建(this)的对象成员称为实例成员,只能由实例化对象访问;
function Person(uname, age) {
// 实例成员定义
this.uname = uname;
this.age = age;
this.sing = function () {};
}
var zs = new Person('zs', 23);
console.log(zs.uname); // 实例成员访问
Person.sex = 'woman'; // 静态成员定义
console.log(Person.sex); // 静态成员访问
构造函数原型 prototype
构造函数中的复杂数据类型(function)会重新开辟内存空间进行存储,随着实例的增加,会产生性能问题;
构造函数通过原型分配的函数是所有对象共享的;JavaScript 规定,每一个构造函数都有一个 prototype 属性;指向另一个对象,注意: 这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有;我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有的对象的实例就可以共享这些方法
function Person() {
this.sing = function () {}
}
Person.prototype.song = function () {console.log('增加共享方法')}
var zs = new Person();
var ls = new Person();
console.log(zs.sing === ls.sing); // false 因为每个实例的对象中的复杂数据类型,都会存放在不同的内存空间
zs.song(); // prototype 方法调用
console.log(zs.song === ls.song); // true 通过原型对象 prototype 共享了 song 方法
对象原型 __proto__
对象都会有一个属性
__proto__指向构造函数的 prototype 原型对象,之所以对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有__proto__原型的存在
__proto__对象原型和原型对象 prototype 是等价的
console.log(zs.__proto__ === Person.prototype); // true
__proto__对象原型的意义就在于为对象查找机制提供一个方向,或者说一条链路,但是它是一个非标准属性,实际开发中不可以使用,它只是内部指向原型对象的 prototype

constructor 构造函数
对象原型(
__prpto__)和构造函数 (prototype)原型对象里面都有一个 constructor 属性,constructor 我们称为构造函数,因为它指回对象本身;constructor 主要用于记录该对象引用了哪一个构造函数,它可以让原型对象重新指回原来的构造函数
function Person() {}
// 如果以赋值的形式给原型对象(prototype)添加方法,此时原型对象(prototype)与对象原型(__proto__)中存在 constructor 构造函数这个构造函数指向 Person 对象
Person.prototype.song = function () {};
Person.prototype.move = function () {};
// 2. 如果原型以对象的形式赋值,那么此时原型对象(prototype)和对象的原型(__proto__)就不存在 constructor 构造函数,也不指向Person对象本身
Person.prototype = {
// 可以通过键值对的方式,手动指向Person对象
constructor: Person,
sing: function () {},
movie: function () {}
}
var zs = new Person();
console.log(Person.prototype);
console.log(zs.__proto__);
对象,实例,原型对象之间的关系
每一个构造函数都会有一个原型对象 prototype;
构造函数的对象实例的原型
__proto__也会指向这个原型对象 prototype;同时他的constructor属性也会指向这个原型对象;这个原型对象 prototype 的 constructor属性指回这个构造函数;

原型链

JavaScript 成员查找机制
- 当访问一个对象的属性(包括方法)时,首先查找对象自身有没有该属性或者方法
- 如果没有,就查找原型,也就是(
__proto__指向的原型prototype对象) - 如果还没有,就查找原型对象的原型(Object原型对象)
- 依次类推一直找到Object()为止(null)
继承
ES6之前并没有提供 extends 继承,只能通过构造函数 + 原型对象模拟实现继承,这种方法被称为组合继承
call()
call() 可以调用函数,也可以改变函数中this的指向
// call(thisAarg, arg1, arg2); function fn() { console.log('hello'); console.log(this); } var o = {}; fn.call(); // 此时的this指向全局对象 window/global fn.call(o); // 此时的this就指向了 o这个对象
借用构造函数实现继承父类型的属性
通过 call() 方法把父类型的this指向子类型的this
function Father(uname, age) {
this.uname = uname;
this.age = age;
}
function Son(uname, age, score) {
// 这里面的this是Son改变了this的指向
Father.call(this, uname, age);
this.score = score;
}
var zs = new Son('zs', 23, 90);
console.log(zs); // 只能继承父类的属性和实例方法;不能继承使用原型对象prototype声明的方法
借用原型对象实现继承父类的方法
function Father(uname) {
this.uname = uname;
}
Father.prototype.sing = function () {
console.log(this.uname);
}
function Son(uname) {
Father.call(this, uname); // 继承属性
}
// 把 Father的实例对象给Son的原型对象prototype
// Father的实例对象可以通过__proto__原型访问Father实例对象的prototype原型对象的sing()方法
// 同理 Son.prototype 也是可以访问这个sing()方法的;
// 此时 Son.prototype 等同于被一个新对象赋值了; 新对象的__proto__原型指向构造函数Father的原型对象prototype也存在自己的constructor属性; 所以Son.prototype.constructor此时等价于Father.prototype.constructor; 因此需要手动再次指派回来
Son.prototype = new Father(); // 此时会改变Son.prototype.constructor的指向 需要重新指定回来
Son.prototype.constructor = Son; // 手动指向
Son.prototype.song = function () {
console.log(this.uname);
}
var zs = new Son('hello world');
zs.sing();

浙公网安备 33010602011771号