文章主要基于<< Javascrpt 高级程序设计3 >>总结的!!!
PS: 2018/05/09 基本重写了全文,补充知识点,新增实例,优化排版
PS: 2018/05/11 新增检测方法,技巧用法javascript
new constructor(arguments):
建立一个用护定义的对象类型的实例或具备构造函数的内置对象类型之一java
注意点几个:segmentfault
这是基本用法,你们都懂的,而后咱们往深层次里挖掘下底层原理怎麽运做的.
假设有个函数浏览器
function Person(name) { this.name = name || 'mike', this.getAge = function () { console.log(10); } } var man = new Person; var women = new Person('tracy') console.log(man.name, women.name) // mike tracy
当代码执行时会通过几个步骤:安全
如下是我基于JS高程3总结的:函数
如下是原版:优化
下面详细举例
一、若是构造函数不返回任何值则按照其余语言同样返回实例化对象(即步骤1建立的对象).this
function Person() { } var man = new Person(); console.log(man) // Person {}
二、若是构造函数返回的是基本类型 (string, number, boolean, null,undefined) 也按照其余语言同样返回实例化对象(即步骤1建立的对象).若是大家还搞不懂基本类型跟引用对象的区别,能够参考我以前写的文章关於javascript基本类型和引用类型小知识spa
function Person() { return '我是基本类型' } var man = new Person(); console.log(man)//Person {}
三、若返回值是引用类型,则实际返回值为这个引用类型.prototype
function Person() { return { age: 18 } } var man = new Person(); console.log(man) //Object {age: 18}
初学者特别应该注意的是他们之间是不一样的,所谓的构造函数是建立一个用户定义的对象类型的实例或具备构造函数的内置对象类型之一
正常来讲构造函数不须要有返回值的,能够认为构造函数和普通函数的最大差异就是:构造函数中没有return语句,普通函数能够有return语句;构造函数中会使用this关键字定义成员变量和成员方法,普通的函数不会使用this关键字定义成员变量和方法.
像第二种状况即便返回基本类型也会被忽略掉,只有选择返回一个普通对象才会取代整个new出来的结果
每一个函数都有一个 prototype
属性, prototype 就是指向经过调用构造函数而建立的那个对象实例的原型对象,做用是可让全部对象实例共享它所包含的属性和方法.
在JavaScript
中一切皆对象,每一个对象都是继承自另外一个对象,因此对象都有本身的原型对象(除了null之外).
因此除了在构建函数内部定义属性方法共享以外,咱们还能够在构造函数的原型对象上添加共享的自定义属性方法.
function Person() { } //原型链添加函数 Person.prototype.getAge = function () { console.log(18) } //实例化 var man = new Person(), women = new Person(); man.getAge() // 18 women.getAge() // 18
有一种状况是对象实例自身已经赋有同名属性方法会覆盖 prototype 上的属性方法.这个认知是不许确的,下面的原型链会讲到这部分.
function Person() { } //原型链添加函数 Person.prototype.getAge = function () { console.log(18) } //实例化 var man = new Person(); man.getAge = function () { console.log(81) } man.getAge() // 81
还有一种状况是在构造函数的原型对象上添加共享的自定义属性方法而且实例化对象以后,再次修改原型对象上的方法.一样会影响到输出结果,依然下面的原型链会讲到这部分.
function Person() { } //原型链添加函数 Person.prototype.getAge = function () { console.log(18) } //实例化 var man = new Person(); //修改原型链自定义函数 Person.prototype.getAge = function () { console.log(81) } man.getAge() // 81
下面引出JS高程三解析片断----------
理解原型对象(关键词 prototype, constructor, __proto__)
不管什麽时候,只要建立了一个新函数,就会根据一组特定的规则为该函数建立一个prototype
属性,这个属性 指向函数的原型对象.在默认状况下,全部原型对象都会自动得到一个
constructor
(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针.就拿前面的例子来讲,Person.prototype.constructor 指向 Person .而经过这个构造函数,咱们还可继续为原型对象添加其余属性和方法.建立了自定义的构造函数以后,其原型对象默认只会取得 constructor 属性;至于其余方法,则都是从 Object 继承而来的.
当调用构造函数建立一个新实例后,该实例的内部将包含一个
指针(内部属性)
,指向构造函数的原型对象.ECMA-262 第 5 版中管这个指针叫
[[Prototype]]
.虽然在脚本中没有标准的方式访问 [[Prototype]] ,但 Firefox、Safari 和 Chrome 在每一个对象上都支持一个属性__proto__ ;而在其余实现中,这个属性对脚本则是彻底不可见的.不过,要明确的真正重要的一点就是,这个链接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间.
这里引出两个关键知识点:
间单说构造函数、原型和实例的关系:每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针.原型链可让咱们作到利用原型让一个引用类型继承另外一个引用类型的属性和方法.
Javascript 一切皆对象(普通对象和函数对象).每一个函数对象都有一个内部连接到另外一个对象它的原型 prototype.该原型对象有本身的原型,等等,直到达到一个以null为原型的对象.根据定义,null没有原型,而且做为这个原型链 prototype chain中的最终连接.
例如:
function Person() { this.name = 'mike' } var man = new Person; console.log(man.__proto__ === Person.prototype); // true //继续深刻发掘它的原型 console.log(Person.prototype.__proto__ === Object.prototype); // true //前面说的一切皆对象是这个意思 console.log(Function.prototype.__proto__); // Object {} console.log(Array.prototype.__proto__); // Object {} console.log(Number.prototype.__proto__); // Object {} //继续深刻发掘它的最终来源是什麽 console.log(Object.prototype); // Object {} console.log(Object.prototype.__proto__); // null
上面一步一步挖到最初的原型,有个注意的点容易混淆的,再强调一遍prototype是函数对象继承的原型,__proto__是指向建立它的函数对象的原型对象prototype.只有真的弄清楚关系你才知道下面的区别
console.log(Function.prototype); // function () {} console.log(Array.prototype); // [Symbol(Symbol.unscopables): Object] console.log(Number.prototype); // Number {[[PrimitiveValue]]: 0} console.log(Object.prototype); // Object {} //下面函数对象都是经过new Function()建立,因此指向一定都是Function.prototype. console.log(Function.__proto__); console.log(Array.__proto__); console.log(Number.__proto__); console.log(Object.__proto__); //细细品味这句 console.log(Function.__proto__ === Function.prototype); // true
访问一个对象的属性时,它先在该对象上搜寻,若是该对象没有就搜寻该对象的原型,若是尚未就继续往该对象的原型的原型搜索,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾(即undefined)位置.这就是上面说的实例方法覆盖原型对象方法认知不许确的解释了
function Person() { this.name = 'mike' } Person.prototype.age = 10; Person.prototype.getAge = function() { console.log(20) }; var man = new Person; console.log(man.age) // 10 man.getAge() //20 console.log(man.sex) // undefined
原型对象除了__proto__以外还有一个constructor属性,做用是返回对建立此对象的函数的引用.这个比较间单,直接上实例
function Person() { this.name = 'mike' } var man = new Person; console.log(man.__proto__.constructor === Person); // true console.log(new Array().constructor); // [Function: Array] console.log(new Number().constructor); // [Function: Number] console.log(new Object().constructor); // [Function: Object] console.log(new Function().constructor); // [Function: Function]
实例化对象man打印结果以下.
man.__proto__.constructor === Person
上面获取原型对象的方法其实不安全,
function Person() { this.name = 'mike' } var man = new Person; man.prototype = Object; console.log(man.prototype); // [Function: Object]
因此咱们通常经过Object.getPrototypeOf
方法去获取.
function Person() { this.name = 'mike' } var man = new Person; man.prototype = Object; console.log(man.prototype); // [Function: Object] console.log(Object.getPrototypeOf(man)); // Person {}
若是是想要检测某个对象是否构造函数的实例,可使用instanceof
function Person() { this.name = 'mike' } var man = new Person; console.log(man instanceof Person); // true
还记得上面说的构造函数与其余函数的惟一区别,就在于调用它们的方式不一样.任何函数,只要经过 new 操做符来调用,那它就能够做为构造函数;而任何函数,若是不经过 new 操做符来调用,那它跟普通函数也不会有什麽两样;
用个小技巧可让你没有使用new操做符也能实例化构造函数,为了区别改了点代码.
function Person(name) { //若是是实例化对象直接赋值 if (this instanceof Person) { this.name = name } else { //不然从新实例化 return new Person(name) } } var man = new Person('mike'), women = Person('kitty'); console.log(man.name, women.name); // mike kitty