JS原型函数相关基础知识

函数对象和普通对象

 
 
 
 
//普通对象 var oo1 = {}, oo2 = new Object(), oo3 = []; console.log(typeof oo1 + ',' + typeof oo2 + ',' + typeof oo3); //object,object,object //函数对象 function ff1() {} var ff2 = function () {} var ff3 = new Function(); console.log(typeof ff1 + ',' + typeof ff2 + ',' + typeof ff3); //function,function,function console.log(typeof Object + ',' + typeof Function + ',' + typeof Array + ',' + typeof Date); //function,function,function,function

如上typeof返回值为object的认为普通对象,typeof返回值为function的认为函数对象,其主要差异以下:javascript

  1. 每个函数对象都有一个显示的prototype属性,它表明了对象的原型(Function.prototype函数对象是个例外,没有prototype属性)。每一个对象都有一个名为proto的内部隐藏属性,指向于它所对应的原型对象(chrome、firefox中名称为proto,而且能够被访问到)。原型链正是基于proto才得以造成(函数对象也有proto属性)。
  2. 函数对象能够做为对象构造器,经过new操做符来返回一个对象。

即函数对象是特殊的对象,它拥有prototype属性,可做为对象构造器。JS提供的对象如Function、Object等都是函数对象。(注意Function是特殊的函数对象,new Fucntion()返回的是一个函数对象,而其它函数对象如Object等经过new操做符——new Object()返回的是一个普通对象)。html

prototype、proto和constructor

prototype是函数对象的一个属性(每一个函数都有一个prototype属性),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。
proto是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,proto是对象的内置属性),是JS内部使用寻找原型链的属性。
每一个函数对象都有名为“prototype”的属性(上面提到过Function.prototype函数对象是个例外,没有prototype属性),用于引用原型对象。此原型对象又有名为“constructor”的属性,它反过来引用函数自己。这是一种循环引用(F4.prototype.constructor===F4)。java

 
 
 
 
var F4 = function () { this.a = 'a'; return this.a; } var o4 = new F4(); console.log(o4.a); //'a' console.log(F4.prototype === o4.__proto__); //true console.log(F4.prototype === F4.__proto__); //false console.log(F4.prototype === Function.prototype); //false console.log(F4.__proto__ === Function.prototype); //true console.log(o4.constructor === F4); //true console.log(o4.constructor === F4.prototype); //false console.log(o4.constructor === F4.prototype.constructor); //true console.log(F4.constructor === Function); //true console.log(F4.constructor === Function.prototype.constructor); //true console.log(Function === Function.prototype.constructor); //true console.log(F4 === F4.prototype.constructor); //true console.log(Object.constructor === Function); //true console.log(Function.prototype === Function.__proto__); //true console.log(typeof Object + ',' + typeof Object.prototype + ',' + Object.prototype.__proto__ + ',' + Object.prototype.prototype); //function,object,null,undefined console.log(typeof Function + ',' + typeof Function.prototype + ',' + Function.prototype.__proto__ + ',' + Function.prototype.prototype); //function,function,[Object],undefined console.log(typeof F4 + ',' + typeof F4.prototype + ',' + F4.prototype.__proto__ + ',' + F4.prototype.prototype); //function,function,[Object],undefined console.log(typeof o4 + ',' + typeof o4.__proto__ + ',' + o4.__proto__.__proto__ + ',' + o4.__proto__.prototype); //object,object,[Object],undefined


上图中o为普通对象,F为普通函数对象,椭圆表示函数对象,矩形表示函数对象,从中能够看出:chrome

  1. 全部对象,包括函数对象的原型链最终都指向了Object.prototype,而Object.prototype.proto===null,原型链至此结束。
  2. Function.prototype是一个函数对象,可是Function.prototype却没有prototype属性,即Function.prototype.prototype===undefined,因此Function.prototype函数对象是一个特例,没有prototype属性。除Function.prototype外,其它函数对象如Object.prototype
  3. Object.constructor===Function;说明Object是Function函数构造出来的。编程

    函数对象的特殊属性

    arguments

    arguments其主要用途是用来保存函数参数。 数组

       
       
       
       
    arguments.length//值来判断实参的个数 arguments[0]//表示传递的第一个元素 arguments[1]//表示传递的第二个元素

    arguments(不是Array的实例)对象还有一个属性:callee,指向拥有arguments对象的函数,在编写递归函数时经常使用。app

       
       
       
       
    function factorial(num){if(num<=1){ return 1;}else{ //return num*factorial(num-1); //一个实例化的函数对象能够拥有不少个名字,不该限定函数名必须是factorial return num*arguments.callee(num-1);}}

    length

    length表示每一个函数准备接收的命名参数的个数,即形参的个数,区别于内部对象arguments是实参的个数。 函数

       
       
       
       
    function sum(num1,num2,num3){ alert(arguments.callee.length);//方法2:内部对象调用}sum(1,2)alert(sum.length);//方法1:函数调用

    this对象

    this指向调用当前函数的对象,当在网页全局做用域中调用时,this对象引用的就是window。性能

       
       
       
       
    function hello(){ console.log('hello '+this.val);}var a={"val":"a","hello":hello},b={"val":"b","hello":hello};hello();//hello undefineda.hello();//hello ab.hello();//hello b

    call,apply, bind

    apply方法有两个参数,1个是在其中运行函数的做用域(即要调用函数的对象),另外一个是参数数组。
    call方法与apply方法的做用同样,可是接收参数的方式不一样,其参数必须逐个例出来。
    bind方法新建立一个函数对象,这个函数对象的this值绑定参数传入的对象。
    call 和 apply差异:参数的样子不同,另外一个就是性能不同。(apply的性能要差不少,可到 JSPerf 上去跑跑看看)
    注意,在bind后,call、apply方法也不能改变this指针,以下面的示例this

       
       
       
       
    hello.call(a,'pa');//hello a pahello.apply(a,['pa']);//hello a pahello.call(b,'pb');//hello b pbhello.apply(b,['pb']);//hello b pbvar h = hello.bind(a);hello();//hello undefined undefinedh();//hello a undefinedh.call(b,'pb');//hello a pbh.apply(b,['pb']);//hello a pb

    new关键字

       
       
       
       
    var Base=function(){}var obj=new Base();//上面中new操做符干了三件事情//1.初始化一个空对象obj var obj = {}; //2.将这个空对象的__proto__成员指向了Base函数对象prototype成员对象obj.__proto__ = Base.prototype;//3.将Base函数对象的this指针替换成obj,而后再调用Base函数,初始化objBase.call(obj);

    但要注意但函数自己返回值为对象时new操做符不起做用。

    若是函数返回值为常规意义上的值类型(Number、String、Boolean)时,new函数将会返回一个该函数的实例对象,而若是函数返回一个引用类型(Object、Array、Function),则new函数与直接调用函数产生的结果等同。

 
 
 
 
var F3 = function () { this.a = 'a'; var o = { 'name' : 'name' }; return o; } var o3 = new F3(); console.log(o3.a); //undefined console.log(o3.__proto__ === F3.prototype); //false console.log(F3.prototype == F3.__proto__); //false console.log(F3.__proto__ == Function.prototype); //true console.log(o3.constructor === F3); //false; var F4 = function () { this.a = 'a'; return this.a; } var o4 = new F4(); console.log(o4.a); //'a' console.log(F4.prototype === o4.__proto__); //true console.log(F4.prototype === F4.__proto__); //false console.log(F4.prototype === Function.prototype); //false console.log(F4.__proto__ === Function.prototype); //true console.log(o4.constructor === F4); //true console.log(o4.constructor === F4.prototype); //false console.log(o4.constructor === F4.prototype.constructor); //true

JS面向对象编程时类函数定义方式

 
 
 
 
var F2 = function () { var inner='inner'; this.o1 = 'o1'; this.f1 = function () { console.log('f1 ' + this.o1+' '+ inner); } function f2() { console.log('f2 ' + this.o1+' '+ inner); } } F2.prototype.f3 = function () { //console.log('f3' + this.o1+' '+ inner);失败,找不到inner console.log('f3' + this.o1); } var o2 = new F2(); o2.f1(); //o2.f2();失败,找不到f2 o2.f3();

js面向对象编程时类定义函数用prototype方式和this方式区别:this方式定义的函数如f1能够访问构造函数内部的变量如上例inner,prototype方式定义的函数f3则不能访问内部变量; 但若是全部函数都采用this方式定义则每次实例化都要执行,浪费内存,也不合理。
this方式定义方法叫作特权方法,主要是为了访问内部的私有字段,这样就能够控制对某些字段的访问。prototype方式定义的函数能访问这些特权方法,进而间接访问私有字段。

所以,若是要直接访问私有字段,应该使用特权方法,也就是this定义的方法,应该定义在构造函数内部。相反,若是不须要直接访问私有字段,应该使用prototype定义的方法,定义在构造函数外部。

参考:
Js中Prototype、proto、Constructor、Object、Function关系介绍
Js New一个函数和直接调用的区别



相关文章
相关标签/搜索