*原型链(面向对象的一个基础)
1.创建对象有几种方法
1、从结果上看这两种算是一类,写法不一样
var o1 ={name:'o1'};//字面量对象--默认这个对象原型链指向Object
var o2 = new Object({name:'o2'}); //通过new Object声明的一个对象
2、使用显式构造函数创建对象(通过构造函数)
var M = function(name){ this.name = name;};
var o3 = new M('o3');
3、Object.create方法创建
//创建一个具有指定原型且可选择性地包含指定属性的对象
var P ={name:'p'};
//Object.create方法是把参数中这个对象作为一个新对象的原型对象赋给o4的,
o4本身不具备这个属性的,通过原型链来连接这个原型对象的。(所以o4对象本身没有
name这个属性,只能通过原型链来找name属性。)
var o4 = Object.create(P);
--o4.__proto__指向的就是P对象
------->o4.__proto__ ===P; //true
o4创建出来是一个空对象,空对象本身没有name这个属性,它是在它的原型对象上。
------>console
-->o1 Object{name:"o1"};
-->o2 Object{name:"o2"};
-->o3 M{name:"o3"};
-->o4 Object{} //空对象
但是o4.name-->"p"
Object.create方法创建的这个对象,是用原型链来连接的;
2.原型、构造函数、对象实例、原型链
1.实例:
只要是对象就是一个实例;
2.构造函数:
凡是通过new来操作后边这个函数,这个函数就是构造函数;
(任何一个函数只要被new使用了,后面这个函数就可以被叫做构造函数。)
构造函数可以使用new运算符来生成一个实例;构造函数也是函数;
任何函数都可以用来当做构造函数,只要用new操作,立马就变成构造函数;
不用new就是普通函数。
3.原型对象
函数都有一个prototype属性,在声明一个函数js自动加上去的属性,这个prototype
指的就是原型对象;
在声明一个函数,开始写function xxx,js引擎给这个函数自动增加一个属性,这个属性就是
构造函数prototype属性,这个属性会初始化一个空对象,也就是这个原型对象。
3.1.原型对象怎么区分是被那个构造函数所引用呢?
constructor 构造器
原型对象中会有一个构造器,这个构造器会默认你声明的那个函数。
eg:var M = function(name){ this.name = name; };
var o3 = new M('o3');
M是构造函数 o3是实例
---->console
o3-->M{name:"o3"}
M--->function(name){this.name = name;}
M.prototype(原型对象)
--->Object{ constructor:function(name); __proto__:Object}
(是一个空对象,(原型对象)有constructor属性,这个属性指向了一个函数,这个函数就是构造函数。)
M.prototype.constructor === M;//ture
----1.构造函数通过new和实例关联;
2.构造函数的原型对象通过constructor和构造函数关联;
3.**实例和原型对象是一个什么关系?(实例对象的__proto__这个属性引用的是M的原型对象;)
o3.__proto__ === M.prototype ; //true
4.原型链
从一个实例对象往上找,构造这个实例的相关联的对象,然后这个关联对象再往上找,又有创造它的上一级的
原型对象,以此类推,一直到Object.prototype 原型对象终止,这个链条就断了,也就是说Object.prototype
属性是整个原型链的顶端,到那里就截止了。
4.1.1原型链是通过什么来实现这个往上找的过程呢?
通过prototype这个原型和__proto__属性来完成原型链的查找的;
4.1.2原型对象和原型链之间起的什么作用?
eg:构造函数中增加了很多属性和方法,实例是不是就可以共用这个东西了?
当多个实例的时候,想去共用这个方法的时候,不能每个实例都copy一份,他们有相同的方法,可以存到一个共同的东西上,
这个共同的东西就是原型对象。
eg:var M = function(name){this.name=name;};
var o3 = new M('o3');
如果在this.name下面写this.say=function(){}方法,这样每个实例把这个东西都copy一份,占内存,没必要;
4.2.1:原型链的功能
通过在M的原型链上(原型对象上)增加方法以后,通过原型链的方式使这个对象可以找到这个原型对象的方法,实例也是可以
拥有的,这就是js引擎做的原型链的基本功能;
4.2.2原型的基本原理
任何一个实例对象通过原型链找到它上面的原型对象,上面的方法和属性都是被实例所共享的。
eg:M.prototype.say = function(){
console.log('say hi');
};
var o5 = new M('o5');
------->console
o3.say()---->say hi
o5.say()---->say hi
**原型链的工作原理:通过原型链的方式,找到原型对象,原型对象的方法是被不同的实例共有的。
**按照js引擎分析的方式,在访问一个实例的时候,这个实例上有什么方法,在这个实例本身没找到这个方法和属性,它会往它的原型
对象上找,通过__proto__上一级原型对象去找,如果它上一级对象还没有找到这个属性和方法,它会在它原型对象的基础上,在通过
原型对象的__proto__,在往上一级查找,依次类推,直到找到Object.prototype,如果还没找到,原路返回,告诉它这个属性和方法没有
找到、没有定义;如果在中间任一环节找到了,就会停止网上查找,直接返回这个方法;
总结:
1.构造函数(函数)才会有prototype,对象没有prototype;
2.只有实例对象有__proto__;
函数既是一个函数也是一个对象,所以也会有这个属性。它的属性是:
M.__proto__ ===Function.prototype;//true
--->M的构造函数是Function;(M这个普通函数是Function这个构造函数的一个实例;)
4.3.实例被构造函数生成的,它怎么跟这个原型对象产生关联呢?
构造函数的prototype这个属性,你修改这个属性,也就修改了实例所在上一级的原型对象。
3.instanceof 的原理
实例对象上有__proto__这个属性,实例对象的这个属性引用是它构造函数的原型对象(也就是找到的这个构造函数);
构造函数有prototype这个属性,这个属性引用的原型对象,在往下走,实例对象的__proto__这个属性,其实是引用这个原型对象。
虽然实例对象是一个构造函数产生的,但是这个属性(实例对象的__proto__属性)和构造函数本身没有关联,它关联的是构造函数下面的
一个属性prototype所引用的原型对象。
原理:就是来判断实例对象的__proto__这个属性和构造函数的prototype这个属性是不是同一个引用。
(实例对象的.__proto__ )instanceof (构造函数的prototype;
判断这个实例对象是不是这个构造函数的实例的时候,其实判断的是实例对象下面的属性.__proto__与构造函数下的属性prototype是不是引用的同一个地址;
如果是,instanceof成立了返回true;如果不是返回false;
o3 instanceof M;//true
o3 instanceof Object;//true
--只要是在原型链上的构造函数都会被instanceof看成o3的一个构造函数;
03.__proto__ ===M.prototype;//true
M.prototype.__proto__ === Object.prototype; //true
(原型对象的__proto__指向的是Object的prototype,Object这个构造函数也会被instanceof看做是o3的一个构造函数)
o3.__proto__.constructor ===M;//true;o3是M这个构造函数直接生成的实例对象
o3.__proto__.constructor === Object; //false
---用constructor判断比用instanceof更加严谨;
**constructor来判断一个实例对象从严谨意义上是不是构造函数直接生成的实例对象,是用原型对象的constructor来构造,实例对象和构造函数是一个
桥梁关系,但constructor是放在prototype下的constructor属性来判断的。
4.new 运算符
一个新对象被创建,它继承自(这个构造函数的原型对象)foo.prototype
|
|
构造函数foo被执行,执行的时候,相应的传参会被传入,同时上下文(this)
会被指定为这个新实例;new foo等同于new foo(),只能用在不传递任何参数的情况
|
|
如果构造函数返回了一个“对象”,那么这个对象会取代这个new出来的结果,如果构造函数
没有返回对象,那么new出来的结果为步骤1创建的对象(一般情况下构造函数不返回任何值,
不过用户如果想覆盖这个返回值,可以自己选择返回一个普通对象来覆盖。当然,返回数组也会覆盖,因为数组也是对象。)
eg:
var M = function(name){
this.name=name;
};
M.prototype.say = function(){
console.log('say hi');
};
var o5 = new M('o5');
var new2 = function(func){
//1.创建一个空对象,这个对象要继承这个构造函数的原型对象(空对象要关联构造函数的原型对象;)
var o = Object.create(func.prototype);
//2.执行构造函数
var k = func.call(o);//call用来转移上下文(this),把这个上下文转成o对象
//3.判断构造函数的运行结果是不是对象类型
if(typeof k ==='object'){
return k;
}else{
return o;
}
};
------------->console
--o6 = new2(M);
--->M{name:undefined} __proto__:object
o6 instanceof M; //true
o6 instanceof Object; //true
o6.__proto_-.constructor === M; //true
M.prototype.walk=functon(){
console.log('walk');
};