1. 基本概念
实例对象: 通过实例的方式(new)构造出来的对象
原型对象:所有实例的公共祖先,在实例对象上可以得到原型对象的属性和方法
构造函数:可以通过new关键子执行的函数
//Foo是一个构造函数
function Foo(){
this.a = '1'
}
//foo 是通过new构造的一个实例对象
var foo = new Foo()
2. prototype 、proto 、constructor之间的关系
prototype即原型,原型的访问方式有
(1)通过构造函数的方式 Foo.prototype,该方法适合修改原型
(2)通过实例的方式 foo.proto
(3)ES6方式 Object.getprototypeOf(foo)
function Foo(){
this.a = 1
}
//原型的重写
Foo.prototype = { //这是构造函数的原型
aa: 10,
bb: function(){
console.log(20);
}
}
var foo = new Foo(); //这是一个实例对象
console.log(foo.aa); //10 ,实例对象可以拿到原型的属性
console.log(foo.a); //1
console.log(foo);
console.log(foo.__proto__); //获得其原型对象{aa: 10, bb: ƒ}
constructor 是原型上的一个属性,它指向的是构造函数本身
//例如
function Foo(){
this.a = 1
}
var foo = new Foo(); //这是一个实例对象
console.log(foo.__proto__); //获得该实例的原型队形
输出结果:
{constructor: ƒ}
console.log(Foo.prototype.constructor === Foo); //true
一旦更改原型,constructor改变
//重写原型
Foo.prototype = { //这是构造函数的原型
aa: 10,
bb: function(){
console.log(20);
}
}
var foo = new Foo(); //这是一个实例
console.log(Foo.prototype.constructor);
输出
ƒ Object()
原型链为:
Foo 是本身没有constructor属性 ——(父亲)原型 Prototype,依旧没有constructor属性 —— 爷爷prototype constructot 属性值是Object
console.log(Foo.prototype.constructor);
根据原型链搜索
Foo重写后没有 constructor 然后找它的父亲,仍然没有constructor ,再找他爷爷,找到了,原型链继承得到。
注意:
当重写原型时,为其加上constructor属性,指向这个构造函数
Foo.prototype = { //这是构造函数的原型
aa: 10,
bb: function(){
console.log(20);
}
constructor: Foo
}
总结:
prototype和__proto__都是指向的原型,只是访问方法不同
constructot 指向的时构造函数
3. 对象的继承
-
原型链继承
根据原型链继承——实例对象可以拿到原型对象的属性和方法——也能拿到原型的原型的属性和方法,一级级继承得到。
<script>
function Super() {
this.a = '1'
}
Super.prototype.say = function(){
console.log(222);
}
function Sub(){
}
//Sub的原型是一个Super实例,所以Sub.prototype本身具有了属性a,同时可以访问其原型Super.prototype的方法
Sub.prototype = new Super();
var sub1 = new Sub(); //它具有原型
console.log(sub1.a);
console.log(sub1.say);
</script>
解释:(—>表示原型是)
实例对象sub1—>Sub.prototype(只是一个new super实例对象,所以可以拿到实例上的属性),并且通过Super.prototypy可以访问Super 的原型对象方法
出现问题:
引用值(String,Object类型)共享问题)
function Super() {
this.a = [1,2,3]
}
//在sub1.a的属性值中加入新的数
sub1.a.push(4)
console.log(sup.a); //[1, 2, 3]
console.log(sub1.a); //[1, 2, 3, 4]
console.log(sub2.a); //[1, 2, 3, 4]
- 构造函数继承——解决引用值的共享问题
此继承存在的问题:没办法拿到原型上的方法
<script>
function Super() {
this.a = [1, 2, 3]
}
Super.prototype.say = function () {
console.log(222);
}
function Sub() {
Super.call(this); //这里的this指向创建的Sub对象
}
//实现互不影响
var sub1 = new Sub();
// 执行过程,sub1为一个实例对象,在Sub构造函数中,this指向这个对象,
// 执行Super.call(this) === Super(sub1) 到函数(此处没有和new 搭配使用
// ,其实是个普通函数吧,所有没发拿到原型上的方法)内执行 this.a === sub1.a
var sub2 = new Sub();
// 执行过程,sub2为一个实例对象,在Sub构造函数中,this指向这个对象,
// 执行Super.call(this) === Super(sub2) 到函数内执行 this.a === sub2.a
</script>
- 组合继承的方式(原型链与构造数组的结合)又叫伪经典继承
组合其优点
(1) 解决引用值的共享问题
(2) 解决原型方法和属性调用问题
<script>
function Super() {
this.a = [1, 2, 3]
}
Super.prototype.say = function () {
console.log(222);
}
function Sub() {
Super.call(this); //这里的this指向创建的Sub对象
}
// 新增——原型链关键语句
Sub.prototype = new Super();
//每次创建Sub实例对象,都会调用两次Super函数
var sub1 = new Sub();
var sub2 = new Sub();
</script>