JavaScript是一种基于对象的语言,可以说万物皆对象。
特别说明:基于对象也好,面向对象也好实际上都是以对象的概念来编写程序,从本质上并无区别。
JavaScript没有类这个概念,它继承机制是基于原型,产生原型链的模式,而不是其他语言的类。因此要理解JavaScript的继承机制,需要更深入了解原型对象的概念。
对象
JavaScript中,对象是有区别的,分为普通对象和函数对象,Object、Function是JS自带的函数对象,function定义方式本质上还是new Function方式。(函数对象都是通过Function来实例化的)
<script type="text/javascript">
function f1(){};
var f2 = function(){};
var f3 = new Function('str', 'console.log(str)');
var o3 = new f1();
var o1 = {};
var o2 = new Object();
console.log(typeof Object);//function
console.log(typeof Function);//function
console.log(typeof o1);//object
console.log(typeof o2);//object
console.log(typeof o3);//object
console.log(typeof f1);//function
console.log(typeof f2);//function
console.log(typeof f3);//function
</script>
在上面的例子中 o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。怎么区分,其实很简单,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object 也都是通过 new Function()创建的。
对象继承
Brendan Eich参考C++和Java,做了简化设计,将new命令引入到JavaScript中,new后面跟对象的构造函数,用来创建对象,缺点:无法共享方法和属性。
比如,在DOG对象的构造函数中,设置一个实例对象的共有属性species。(函数也是一个对象)
function DOG(name) {
this.name = name;
this.species = '犬科';
};
用上面**函数对象**生成两个实例对象
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
这两个对象species属性是独立的,修改其中一个,不会影响到另一个。
dogA.species = '猫科';
alert(dogB.species);//犬科
每个实例对象都有自己的属性和方法的副本,这不仅无法做到资源共享,也极大的资源浪费。
Brendan Eich决定为构造函数设置一个prototype属性。这个属性包含一个对象,所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就在构造函数里面。实例对象一旦创建,将自动引用prototype对象的属性和方法(也就是__proto__
)。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
species属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响两个实例对象。
DOG.prototype.species = '猫科';
alert(dogA.species);//猫科 (dogA的__proto__
指向DOG.prototype)
alert(dogB.species);//猫科
由于所有实例对象共享一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。
原型prototype
在JavaScript中,每当定义一个对象(函数)时候,对象都会包含一些预定的属性。其中函数对象的一个属性就是原型对象prototype(原型对象还有constructor属性(指向构造函数对象))。普通对象没有prototype,但是有__proto__
属性。(比如实例化、字面量的对象)(指向其原型链)
原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性))。
function f1(){};
console.log(f1.prototype);//f1{} (f1{}表示普通对象)
console.log(typeof f1.prototype);//object
console.log(typeof Function.prototype);//function,这个特殊
console.log(typeof Object.prototype);//object
console.log(typeof Function.prototype.prototype);//undefined
打印关于f1函数对象的相关信息
原型链
JS在创建对象(不论普通对象还是函数对象)的时候,都有一个叫做__proto__([[ prototype ]] )
的内置属性,用于指向创建它 的 函数对象的原型对象prototype。
(ps:函数对象来自Function对象,字面量对象来自Object对象,实例对象来自创建它的函数对象-->等等类似)
var person = function(name) {
this.name = name;
};
person.prototype.getName = function() {
return this.name;
}
var gz = new person('gongzi');
gz.getName();//gongzi
以上面的例子为例:
console.log(gz.__proto__ === person.prototype) //true
同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype
console.log(person.prototype.__proto__ === Object.prototype) //true
继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null
console.log(Object.prototype.__proto__) //null
我们把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链。
function Fruits(name) {
this.name = name;
}
Fruits.prototype.colour = 'red';
Object.prototype.taste = '甜';
var fruits = new Fruits('苹果');
console.log(fruits);//打印实例对象(普通对象)
var Game = {
name:'王者荣耀'
};
Object.prototype.type = '竞技';
console.log(Game);//打印字面量对象(普通对象)
console.log(Fruits.__proto__);//打印 创建它 的 函数对象 的原型对象,结果function () {}
console.log(Fruits.constructor);//可以打印出构造函数对象Function//Fruits.__proto__.constructor一样
Function对象作为一个函数,就会有prototype属性,该属性将对应”function () {}”对象。
alert(Fruits.__proto__ === Function.prototype);//true
前面有提到,原型对象属于普通对象。Function.prototype是个例外,它是原型对象,却又是函数对象,作为一个函数对象,它又没有prototype属性。
constructor属性
constructor 属性返回对创建此对象的函数的引用
prototype对象有一个construction属性,默认指向prototype对象所在的构造函数。
由于construction属性定义在prototype对象上面,意味着可以被所有实例对象继承。
constructor属性的作用,是分辨原型对象到底属于哪个构造函数。
总结
1.原型和原型链是js实现继承的一种模式。
2.原型链的形成真正的是靠proto而非prototype。
访问原型对象的方法
获取实例对象的原型对象,有三种方法:
1.obj.construction.prototype或者C.prototype用于引用new C()创建的对象的原型对象
2.Object.getPrototypeOf(obj):是获取obj对象的原型对象的标准方法。
3.obj.proto:是获取obj对象的原型对象的非标准方法,每个对象都有的属性,可以在不支持Object.getPrototypeOf方法时作为权宜之计,proto是非安全的。
上面三种方法第一种和第三种都不是很可靠。最新的ES6标准规定,proto属性只有浏览器才需要部署,其他环境可以不部署,obj.construction.prototype在手动改变原型对象时,可能会失效。
扩展
1.Object.__proto__ === Function.prototype//true
2.Function.__proto__ === Function.prototype//true
3. Function.prototype.__proto__ === Object.prototype//true
1.Object是函数对象,是通过new Funciton()创建,所以Object.__proto__指向Function.prototype。
2.Function也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。
3.Function.prototype是个函数对象,理论上其__proto__应该指向Functuin.prototype,就是它自己,自己指向自己,没有意义。函数对象也是对象,给它设定根指向Object.prototype,Object.prototype.__proto__ === null,保证原型链能够正常结束。
Object.create()
创建一个具有指定原型且可选择性地包含指定属性的对象。
Object.create(prototype, descriptors)
这种创建对象的方式与上面所说略有差异,如果要查找原型链,按照原型对象的类型分析就可以。
是使用第一个参数作为它们的原型。
prototype
必需。要用作原型的对象。可以为 null,不继承任何对象。
descriptors
可选。包含一个或多个属性描述符的 JavaScript 对象。
“数据属性”是可获取且可设置值的属性。 数据属性描述符包含 value 特性,以及 writable、enumerable 和 configurable 特性。
使用方法可以参考网络上的这两篇,或自己百度,没时间细说了。
javascript一种新的对象创建方式-Object.create()
前端开发者进阶之ECMAScript新特性【一】--Object.create
参考文献:
Javascript继承机制的设计思想
JavaScript原型及原型链详解
JS原型、原型链深入理解
实在不懂就就就.............这样记住吧
JS中所有函数的原型都是Function.prototype,所有对象原型链的终点指向Object.prototype