一般构造函数首字母须要大写,主要是为了区别ECMAScript的其它函数。(高程三 P145)javascript
构造函数与其余函数的惟一区别,就在于调用它们的方式不一样。只要经过new来调用,任何函数都是构造函数;而任何函数,若是不经过new来调用,那么它和普通函数也没有任何区别。(P146)html
所谓"构造函数",其实就是一个普通函数,可是内部使用了this变量。对构造函数使用new运算符,就能生成实例,而且this变量会绑定在实例对象上。java
(就是一个普通的函数,与其余函数没有任何区别,能够理解为 函数==构造函数,它只是概念上的一个定义,使用它用来实例化对象。)web
对于JavaScript的内置对象,Object、Array、Date等等这些都是构造函数。express
阮一峰 继承机制的设计思想 __proto__ VS. prototype in JavaScript 深刻理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)编程
_proto_segmentfault
The use of __proto__
is controversial, and has been discouraged(有争议,不被鼓励的). It was never originally included in the EcmaScript language spec, but modern browsers decided to implement it anyway. Only recently, the __proto__
property has been standardized in the ECMAScript 2015 language specification for web browsers to ensure compatibility, so will be supported into the future. 数据结构
_proto_
是全部对象(包括函数)都有的,它才叫作对象的原型,原型链就是靠它造成的。(若是不是实在没有别的办法,是很是不建议在代码中使用的。)app
全部构造器/函数(函数也是对象)的__proto__都指向Function.prototype,它是一个空函数(Empty function)编程语言
Number.__proto__ === Function.prototype // true
Boolean.__proto__ === Function.prototype // true
String.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype // true
Array.__proto__ === Function.prototype // true
RegExp.__proto__ === Function.prototype // true
Error.__proto__ === Function.prototype // true
Date.__proto__ === Function.prototype // true
JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎建立,Math,JSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype。以下
Math.__proto__ === Object.prototype // true
JSON.__proto__ === Object.prototype // true
上面说的“全部构造器/函数”固然包括自定义的。以下
// 函数声明
function Person() {}
// 函数表达式
var Man = function() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype) // true
函数也是对象,构造函数也是对象,能够理解为:构造函数是由“Function构造函数“实例化出来的函数对象。
这说明什么呢?
全部的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。全部构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)。
知道了全部构造器(含内置及自定义)的__proto__都是Function.prototype,那Function.prototype的__proto__是谁呢?
相信都据说过JavaScript中函数也是一等公民,那从哪能体现呢?以下
console.log(Function.prototype.__proto__ === Object.prototype)
这说明全部的构造器也都是一个普通JS对象,能够给构造器添加/删除属性等。同时它也继承了Object.prototype上的全部方法:toString、valueOf、hasOwnProperty等。
最后Object.prototype的__proto__是谁?
Object.prototype.__proto__ ===
null
// true
已经到顶了,为null。
实例是没有prototype属性的,prototype
只有函数(准确地说是构造函数)才有的。它跟原型链没有半毛钱关系。
它的做用是:构造函数new对象的时候,告诉构造函数新建立的对象的原型是谁。是的,只在new一个对象的时候才起做用。当你new完获得这个对象后,随便你怎么改构造函数的prototype
属性,都不会影响已建立的对象的原型链。
function Father(){ // Code goes here
} var obj1 = new Father(); // 此时,
Father.prototype = { name:"hhhhh", last:"wwwww", sayName:function(){ alert(this.name); } }
obj1.sayName() // error
// 由于,构造函数的prototype属性被重写了,js的对象都是不相等的
调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改成另一个对象就等于切断了构造函数与最初原型之间的联系。实例中的指针仅指向原型,而不指向构造函数。
不过通常来讲,咱们会使用__<内部属性名>__ 下划线来代替双括号,例如__proto__(这是某些脚本引擎好比SpiderMonkey的对于原型概念的具体实现,尽管并不是标准).
若是一个对象的prototype没有显示的声明过或定义过,那么__proto__的默认值就是object.prototype, 而object.prototype也会有一个__proto__, 这个就是原型链的终点了,被设置为null。
Object.prototype The Object.prototype
property (是对象的属性)represents the Object
prototype object. (prototype对象)
Javascript规定,每个构造函数都有一个prototype属性,指向另外一个对象。这个对象的全部属性和方法,都会被构造函数的实例继承。
这意味着,咱们能够把那些不变的属性和方法,直接定义在prototype对象上。
这个属性包含一个对象(如下简称"prototype对象"),全部实例对象须要共享的属性和方法,都放在这个对象里面;那些不须要共享的属性和方法,就放在构造函数里面。
实例对象一旦建立,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分红两种,一种是本地的,另外一种是引用(prototype对象)的。
参考资料 JavaScript中__proto__与prototype的关系 理解js中的原型链,prototype与__proto__的关系 参考资料三
原型链如图所示:
MDN Object.prototype.constructor
Returns a reference to the Object
constructor function that created the instance object(返回生成该实例的构造函数). Note that the value of this property is a reference to the function itself, not a string containing the function's name.
constructor是部署在Object.prototype上的属性,因此位于原型链的最高一层(再高一层是null),任何对象(包括函数,都有constructor属性)
任何一个prototype对象都有一个constructor属性,指向它的构造函数。
eg: Cat.prototype.constructor = Cat;
每个实例也有一个constructor属性(原型包含constructor属性,所以能够经过对象实例访问),默认调用prototype对象的constructor属性,即也指向它的构造函数.
eg: var new cat = Cat(){};
cat.constructor = Cat;
修改:添加删除属性仍是在原来的prototype对象上作的修改。
重写:直接就用新的字面量对象来替换了,而字面量对象的constructor就是Object构造函数
每一个函数都包含两个非继承而来的方法:call() 和 apply()
这两个函数等于设置函数体内this对象的值
call和apply是Function的方法,第一个参数是this,第二个是Function的参数。好比函数里写了this,普通调用这个方法这个this多是window。而若是使用了call()或是apply(),第一个参数写啥,里面的this就是啥。(即改变了this的指向)
call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的,换句话说,就是为了改变函数体内部 this 的指向。由于 JavaScript 的函数存在「定义时上下文」和「运行时上下文」以及「上下文是能够改变的」这样的概念。
普通的函数调用 是隐式的传入 this,call 和 apply 能够显式指定它。
call() 和apply() 真正强大的地方是可以扩充函数赖以运行的做用域,并且好处在于对象不须要与方法有任何的耦合关系。
bind()
在函数代码中使用this时颇有趣,这种状况很难且会致使不少问题。
这种类型的代码中,this值的首要特色(或许是最主要的)是它不是静态的绑定到一个函数。
正如咱们上面曾提到的那样,this是进入上下文时肯定,在一个函数代码中,这个值在每一次彻底不一样。
无论怎样,在代码运行时的this值是不变的,也就是说,由于它不是一个变量,就不可能为其分配一个新值(相反,在Python编程语言中,它明确的定义为对象自己,在运行期间能够不断改变)。
this
只是一个引用别名(referencing alias) - 这个别名只知道当前指向的那个对象, 而这也是最棘手的地方。
this
是一个特殊的标识符关键字 —— 在每一个 function 中自动根据做用域(scope) 肯定, 指向的是这次调用的 “全部者,owner” (即 那个对象)
this
是如何建立的?每调用一次 JavaScript 函数时,都会建立一个新的对象, 其中的信息包括: 传入了哪些参数, 函数是如何调用(invoked)的, 函数是在哪里被调用(called)的,等等。该对象中还有一个重要的属性是 this
引用, 函数是哪一个对象的方法,this
就会自动绑定到该对象。
分析一下call的用法
function say(word) { console.log(world); } say("Hello world"); // 二者是
say.call(window, "Hello world"); // 等效的
每次看见functionName(xxx)的时候,你须要立刻在脑海中把它替换为functionName.call(window,xxxx),
this
的做用域(scope) 与函数定义的位置没有关系, 而是取决于函数在哪里被调用( where they are called from ;i.e. the context)。
function test(){
this.x = 1;
alert(this.x);
}
test(); // 1
其实秘密花园里面总结的不错(与上面的四点是一一对应)
1.当在所有范围内使用 this,它将会指向全局对象。
2.函数调用时,这里 this 也会指向全局对象。
3.方法调用 test.foo(); 这个例子中,this 指向 test 对象。
4.调用构造函数 new foo(); 若是函数倾向于和 new 关键词一块使用,则咱们称这个函数是构造函数。在函数内部,this 指向新建立的对象。
this
的知识点和其余机制同样, this
关键字也遵循一些简单的规则, 若是你了解了这些规则,那就能够用得顺畅一些。下面快速回顾这两节中所学的知识点:
this
指向的是全局对象: this
指向的是父对象(parent object)。
call()
、 apply()
或者 bind()
调用时, this
指向的是传递给这些方法的第一个参数。若是第一个参数是 null
或者不是一个对象, 那么 this
指向的是全局对象。new
操做符来调用一个函数时, this
指向的是新建立的这个对象。this
根据所处的语法做用域指向上级对象(parent object)。了解了这些简单直接的规则,咱们就能够很容易地看出 this
指向的是哪一个对象, 若是指向不正确, 那能够用学过的这些黑科技来搞定。
The new
operator creates an instance of a user-defined object type(?对象类型,仍是理解为不一样的数据结构?) or of one of the built-in object types that has a constructor function.
new 操做符生成一个实例,这个实例是用户自定义的“对象类型”或是内建的对象类型的实例。
Creating a user-defined object requires two steps:
new
.用new操做符生成一个这种对象类型的实例To define an object type, create a function for the object type that specifies its name and properties. An object can have a property that is itself another object.
When the code new Foo(...)
is executed, the following things happen:
Foo.prototype
.Foo
is called with the specified arguments, and with this
bound to the newly created object(构造函数被调用,而且把this关键字绑定到新生成的对象上). new Foo
is equivalent to new
Foo
()
, i.e. if no argument list is specified, Foo
is called without arguments.new
expression. If the constructor function doesn't explicitly return an object(若构造函数没有显示的返回对象,则第一步生成的对象就会被返回), the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.) (高程三,建立对象,new操做符的做用)
test