JavaScript 构造函数 prototype属性和_proto_和原型链 constructor属性 apply(),call()和bind() 关键字this

1.构造函数:

通常构造函数首字母需要大写,主要是为了区别ECMAScript的其它函数。(高程三 P145)

构造函数与其他函数的唯一区别,就在于调用它们的方式不同。只要通过new来调用,任何函数都是构造函数;而任何函数,如果不通过new来调用,那么它和普通函数也没有任何区别。(P146)

所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

(就是一个普通的函数,与其他函数没有任何区别,可以理解为 函数==构造函数,它只是概念上的一个定义,使用它用来实例化对象。)

 对于JavaScript的内置对象,Object、Array、Date等等这些都是构造函数。

 this 上下文只存在两种语义,一种是被当作方法调用,this 指向调用它的对象;一种是作为函数调用,指向 Global 对象(严格模式下为 undefined)。 

2.  prototype(prototype属性)和 _proto_

 阮一峰 继承机制的设计思想   __proto__ VS. prototype in JavaScript   深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)

_proto_是所有对象(包括函数)都有的,它才叫做对象的原型,原型链就是靠它形成的。(如果不是实在没有别的办法,是非常不建议在代码中使用的。)

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);
        }
    }
复制代码

调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。实例中的指针仅指向原型,而不指向构造函数。

 不过一般来说,我们会使用__<内部属性名>__ 下划线来代替双括号,例如__proto__(这是某些脚本引擎比如SpiderMonkey的对于原型概念的具体实现,尽管并非标准).

如果一个对象的prototype没有显示的声明过或定义过,那么__proto__的默认值就是object.prototype, 而object.prototype也会有一个__proto__, 这个就是原型链的终点了,被设置为null。

2.1 prototype

Object.prototype  The Object.prototype property (是对象的属性)represents the Object prototype object. (prototype对象)

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

这个属性包含一个对象(以下简称"prototype对象"),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。

实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用(prototype对象)的。

2.2  _proto_    

参考资料  JavaScript中__proto__与prototype的关系     理解js中的原型链,prototype与__proto__的关系   参考资料三

 原型链如图所示:

 

 

 

3. constructor:(constructor属性)

任何一个prototype对象都有一个constructor属性,指向它的构造函数。

                              eg:     Cat.prototype.constructor = Cat;

每一个实例也有一个constructor属性(原型包含constructor属性,因此可以通过对象实例访问),默认调用prototype对象的constructor属性,即也指向它的构造函数. 

           eg:              var new cat = Cat(){};

                           cat.constructor = Cat;

4.  apply(),call(),bind()

每个函数都包含两个非继承而来的方法: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() 

5.   this关键字  

 参考资料,学会JS的this这一篇就够了,根本不用记  JavaScript中this指针指向的彻底理解  javascript的this用法   掌握js中的this(一)    掌握js中的this(二)  

在函数代码中使用this时很有趣,这种情况很难且会导致很多问题。

这种类型的代码中,this值的首要特点(或许是最主要的)是它不是静态的绑定到一个函数。

正如我们上面曾提到的那样,this是进入上下文时确定,在一个函数代码中,这个值在每一次完全不同。

不管怎样,在代码运行时的this值是不变的,也就是说,因为它不是一个变量,就不可能为其分配一个新值(相反,在Python编程语言中,它明确的定义为对象本身,在运行期间可以不断改变)。

总的原则,那就是this指的是,调用函数的那个对象。
通俗点讲, this 只是一个引用别名(referencing alias) - 这个别名只知道当前指向的那个对象, 而这也是最棘手的地方。
简而言之, this 是一个特殊的标识符关键字 —— 在每个 function 中自动根据作用域(scope) 确定, 指向的是此次调用的 “所有者,owner” (即 那个对象)

this 是如何创建的?每调用一次 JavaScript 函数时,都会创建一个新的对象, 其中的信息包括: 传入了哪些参数, 函数是如何调用(invoked)的, 函数是在哪里被调用(called)的,等等。该对象中还有一个重要的属性是 this 引用, 函数是哪个对象的方法,this 就会自动绑定到该对象。

分析一下call的用法

  • Step1: 把第二个到最后一个参数作为函数执行时要传入的参数
  • Step2: 函数执行时的this指向第一个参数
  • Step3: 在上面这个特殊的上下文中执行函数
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)。

情况一:纯粹的函数调用
这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global。

  function test(){

    this.x = 1;

    alert(this.x);

  }

  test(); // 1

情况二:作为对象方法的调用
 函数还可以作为某个对象的方法调用,这时this就指这个上级对象。
 
 情况三 作为构造函数调用
所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。
 
情况四 apply调用
apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。

其实秘密花园里面总结的不错(与上面的四点是一一对应)
1.当在全部范围内使用 this,它将会指向全局对象。

2.函数调用时,这里 this 也会指向全局对象。

3.方法调用 test.foo(); 这个例子中,this 指向 test 对象。

4.调用构造函数 new foo(); 如果函数倾向于和 new 关键词一块使用,则我们称这个函数是构造函数。在函数内部,this 指向新创建的对象。

关于 this 的知识点

和其他机制一样, this 关键字也遵循一些简单的规则, 如果你了解了这些规则,那就可以用得顺畅一些。下面快速回顾这两节中所学的知识点:

  • 在下列情况下 this 指向的是全局对象: 
    • 在最外层的代码中, 不在任何 function 里面
    • 不是对象方法(method)的函数(method)里面
    • 不是构造函数(constructor)的函数里面
  • 当函数作为父对象的属性被调用时, this 指向的是父对象(parent object)。
  • 当函数通过 call() 、 apply() 或者 bind()调用时, this指向的是传递给这些方法的第一个参数。如果第一个参数是 null或者不是一个对象, 那么 this 指向的是全局对象。
  • 在使用 new 操作符来调用一个函数时, this 指向的是新创建的这个对象。
  • 在 ECMAScript 6 中使用箭头函数时, this 根据所处的语法作用域指向上级对象(parent object)。

了解了这些简单直接的规则,我们就可以很容易地看出 this 指向的是哪个对象, 如果指向不正确, 那可以用学过的这些黑科技来搞定。

(The End)
posted @ 2016-12-15 15:30  沐少白  阅读(285)  评论(0)    收藏  举报