JavaScript原型与原型链

一、普通对象与函数对象

在JavaScript的世界里,万物皆对象。对象分为普通对象和函数对象

function f1() { };
var f2 = function () { };
var f3 = new Function();

var o1 = {};
var o2 = new Object();
var o3 = new f1(); // 等同于下面的o4
var o4 = new new Function();
console.log(typeof Object); //function 
console.log(typeof Function); //function  
console.log(typeof f1); //function 
console.log(typeof f2); //function 
console.log(typeof f3); //function   
console.log(typeof o1); //object 
console.log(typeof o2); //object 
console.log(typeof o3); //object
console.log(typeof o4); //object

凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。
f1、f2、f3、Function、Object本质上都是通过new Function()创建的。
小结:函数对象的typeof为function,普通对象的typeof为object

二、构造函数

function Star (uname,age) {
    this.uname = uname;
    this.age = age;
    this.sing = function(){
    	console.log('我会唱歌');
    }
}
var ldh = new Star('刘德华',18);
var zxy = new Star('张学友',20);

上面的 ldh、zxy 都是 Star的实例,它们都有一个constructor (构造函数)属性,该属性(是一个指针,指回构造函数的本身。主要用于记录该对象引用于哪个构造函数。

 console.log(ldh.constructor == Star); // true
 console.log(zxy.constructor == Star); // true

小结:实例的构造函数属性(constructor)指向构造函数。

三、原型对象

在 JavaScript 中,每当定义一个对象(函数)的时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性(也称显示原型属性,这个属性指向函数的原型对象

1、什么是原型对象呢?
原型对象其实就是普通对象,我们的Star.prototype就是原型对象 (Function.prototype 除外,它是函数对象,但它很特殊,它没有prototype 属性(前面说道函数对象都有prototype 属性))。

console.log(typeof Star.prototype); // object
console.log(typeof Function.prototype) // function,这个特殊
console.log(typeof Object.prototype) // object
console.log(typeof Function.prototype.prototype) //undefined

在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Star)

console.log(Star.prototype.constructor == Star); // true

看到这里就会想到我们上面说的每个实例都有一个constructor属性,而这里又说所有的原型对象都会有一个constructor属性,那么这之间有什么联系呢?

ldh.constructor == Star
Star.prototype.constructor == Star

ldh有constructor属性是因为ldh是Star的实例,同理,Star.prototype有constructor属性是因为Star.prototype也是Star的实例。
小结:原型对象(Star.prototype)是 构造函数(Star)的一个实例。

2、原型对象有什么作用呢?

1、给原型对象添加方法
如果直接给构造函数添加方法,则它不同的实例化对象在调用这个方法的时候会开辟不同的堆空间,造成资源的浪费。我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象实例就可以共享这些方法。

function Star (uname,age) {
    this.uname = uname;
    this.age = age;
}
Star.prototype.sing = function(){
   console.log('我会唱歌');
}
Star.prototype.movie = function(){
    console.log('我会演电影');
}

方法有很多的时候,我们可以把它放到一个对象里面

Star.prototype = {  
    constructor:Star,
    sing:function(){
        console.log('我会唱歌');
 	},
	movie:function(){
		console.log('我会演电影');
	}
}

注意了注意了这个是赋值操作,会把原来的原型对象给覆盖
如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor这个属性指回原来的构造函数。

2、主要作用还是用于继承

function Star (uname,age) {
        this.uname = uname;
        this.age = age;
}
var that;
Star.prototype.sing = function(){
    console.log('我会唱歌');
    that = this;
}
var ldh = new Star('刘德华',18);
ldh.sing();
console.log(that === ldh); // true

(1)在构造函数中this指向的是对象实例 ldh
(2)原型对象里面的this指向的是实例对象ldh

故两次 this 在函数执行时都指向 ldh。

四、__ proto __

JavaScript 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性(也叫隐式原型),用于指向创建它的构造函数的原型对象。
也就是说 ldh 有一个 __proto __属性,它的构造函数是Star,Star的原型对象是Star.prototype

ldh.__proto __ == Star.prototype
zxy.__proto __ == Star.prototype

小结:对象的__proto__(隐式原型)的值为其对应构造函数的prototype(显示原型)的值

五、构造器

我们列举几种常见创建对象的方式,来看看他们的__proto__指向。
1、字面量方式

var obj = {}

它等同于下面这样:
2、构造器方式

var obj = new Object()

obj 是构造函数(Object)的一个实例。所以:

var obj = {};
console.log( obj.__proto__===obj.constructor.prototype); // true

所以:

obj.constructor === Object
obj.__proto__ === Object.prototype

再一次说明了:实例的构造函数属性(constructor)指向构造函数
可以创建对象的构造器不仅仅有 Object,也可以是 Array,Date,Function等。
所以我们也可以用构造函数来创建 Array、 Date、Function

var b = new Array();
b.constructor === Array;
b.__proto__ === Array.prototype;

var c = new Date(); 
c.constructor === Date;
c.__proto__ === Date.prototype;

var d = new Function();
d.constructor === Function;
d.__proto__ === Function.prototype;

这些构造器都是函数对象。本质上都是通过new Function()创建的

Number.__proto__ === Function.prototype  // true
Number.constructor == Function //true

Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true

String.__proto__ === Function.prototype  // true
String.constructor == Function //true


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

Function.__proto__ === Function.prototype // true
Function.constructor == Function //true

Array.__proto__ === Function.prototype   // true
Array.constructor == Function //true

RegExp.__proto__ === Function.prototype  // true
RegExp.constructor == Function //true

Error.__proto__ === Function.prototype   // true
Error.constructor == Function //true

Date.__proto__ === Function.prototype    // true
Date.constructor == Function //true

// 这些构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
// 构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind等。

JavaScript中有内置构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的proto是Object.prototype。如下

Math.__proto__ === Object.prototype  // true
Math.construrctor == Object // true

JSON.__proto__ === Object.prototype  // true
JSON.construrctor == Object //true

六. 函数对象

所有函数对象的proto都指向Function.prototype,它是一个空函数(Empty function)

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object

Function.prototype比较特殊,它是函数对象,类型为function。其它的构造器的prototype都是一个普通对象,类型为object
还有我们自定义的函数对象

// 函数声明
function Star() {}
// 函数表达式
var Star = function() {}
console.log(Star.__proto__ === Function.prototype) // true

1、知道了所有构造器(含内置及自定义)的__proto__都是Function.prototype,那Function.prototype的__proto__是谁呢?
相信都听说过JavaScript中函数也是一等公民,那从哪能体现呢?如下

console.log(Function.prototype.__proto__ === Object.prototype) // true

这说明所有的构造器也都是一个普通 JS 对象,可以给构造器添加/删除属性等。同时它也继承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。

console.log(Number.prototype.__proto__ === Object.prototype) // true
console.log(String.prototype.__proto__ === Object.prototype) // true

2、最后Object.prototype的proto是谁?

Object.prototype.__proto__ === null // true

已经到顶了,为null。说明Object.prototype是整个原型链的终点。

显然看到这里头皮有点发麻,我写到这里头皮都发麻了,容易把自己绕晕,所以建议看到这再返回去看看前面的,清晰一下思路。

七、原型链

小测试来检验一下你理解的怎么样:

1、ldh.proto 是什么?
因为 ldh.proto === ldh 的构造函数.prototype
因为 ldh的构造函数 === Star
所以 ldh.proto === Star.prototype

2、Star.proto 是什么?
因为 Star.proto === Star的构造函数.prototype
因为Star的构造函数 === Function
所以 Star.proto === Function.prototype

3、Star.prototype.proto 是什么?
Star.prototype就是我们前面说的原型对象,是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。
因为一个普通对象的构造函数 === Object
所以 Star.prototype.proto === Object.prototype

4、Object.proto 是什么?
Object.proto === Object.的构造函数.prototype
Object也是一个构造函数,Object的构造函数 === Function
前面我们也提到过

console.log(typeof Object); //function 

Object.proto === Function.prototype

5、Object.prototype.proto 是什么?
Object.prototype 对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。说明Object原型对象是整个原型链的终点。
Object.prototype.proto === null

(1)原型链作用:查找对象的属性(方法)
(2)别名:隐式原型链
(3)访问一个对象的属性时,现在自身属性中查找,找到返回,如果没有,再沿着 __proto__这条链向上查找(找它的原型,也就是__proto__指向的prototype原型对象;找原型对象的原型Object的原型对象;一直到Object为止),找到返回,如果最终没找到,返回undefined
(4) Object原型对象的隐式原型为null,说明Object原型对象是整个原型链的终点。

在这里插入图片描述

八、原型链继承

1、套路 :
(1)定义父类型的构造函数
(2)给父类型的原型添加方法
(3)定义子类型的构造函数
(4)创建父类型的对象赋值给子类型的原型
(5)将子类型原型的构造属性设置为子类型
(6)给子类型原型添加方法
(7)创建子类型的对象,可以调用父类型的方法
2、关键:
子类型的原型为父类型的一个实例对象

在这里插入图片描述

九、疑点解惑

console.log(Object.__proto__ === Function.prototype ) // true

Object 是函数对象,是通过new Function()创建的,所以Object.__proto__指向Function.prototype。

console.log(Function.__proto__ === Function.prototype)  // true

Function 也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。

console.log(Function.prototype.__proto__ === Object.prototype) //true

其实这一点我也有点困惑,不过也可以试着解释一下。
前面说所有的构造器都是一个普通对象,但Function.prototype比较特殊,它是个函数对象,理论上它的__proto__应该指向 Function.prototype,就是它自己,自己指向自己,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给它认个祖宗,指向Object.prototype。Object.prototype.proto === null,保证原型链能够正常结束。

十、总结

1、凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。函数对象的typeof为 function,普通对象的typeof为object
2、实例的构造函数属性(constructor)指向构造函数。
3、原型对象(Star.prototype)是 构造函数(Star)的一个实例,原型对象是普通对象。
4、对象的__proto__(隐式原型)的值为其对应构造函数(构造器)的prototype(显示原型)的值
(1)内置构造器

var obj = {name: 'ldh'}
var arr = [1,2,3]
var reg = /hello/g
var date = new Date
var err = new Error('exception')
console.log(obj.__proto__ === Object.prototype) // true
console.log(arr.__proto__ === Array.prototype)  // true
console.log(reg.__proto__ === RegExp.prototype) // true
console.log(date.__proto__ === Date.prototype)  // true
console.log(err.__proto__ === Error.prototype)  // true

(2)自定义构造器

function Star(name) {
  this.name = name;
}
var ldh = new Star('刘德华')
console.log(ldh.__proto__ === Star.prototype) // true

5、所有函数对象的 proto 都指向 Function.prototype,它是一个空函数(Empty function)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值