zoukankan      html  css  js  c++  java
  • Javascript中的函数、this以及原型

    关于函数

         在Javascript中函数实际上就是一个对象,具有引用类型的特征,所以你可以将函数直接传递给变量,这个变量将表示指向函数“对象"的指针,例如:

    function test(message){
              alert(message);
         }
         var f = test;
         f('hello world');
    你也可以直接将函数申明赋值给变量:
    var f = function(message){
              alert(message);     
         };
         f('hello world');

         在这种情况下,函数申明中可以省略函数名称,因为此时名称已经没有任何意义,我们可直接通过变量f来调用函数。

         通过Function类型,我们可以更好地理解函数即对象:

    var f = new Function("message","alert(message);");
         f('hello world');

    关于this

         this可以看成调用函数的实际作用域上下文。比较以下函数的执行结果:

    复制代码
    function test(){
              this.property = 'hello world';
    
         }
         test();
         alert(window.property);   //由于在全局范围内调用,test函数中的this实际指向全局对象(window)
    
         var obj = {};
         test.call(obj);      //通过call第一个参数指定执行上下文范围,所以test函数中this指向obj实例。
         alert(obj.property);      
    
         var obj2 = {};
         obj2.test2 = test;      //将obj2实例方法test指向 全局test方法
         obj2.test2();            //由于是在obj2上调用test方法,所以test函数中的this也指向了obj2实例
         alert(obj2.property);
    复制代码

    定义类型

         在Javascript中可以定义构造函数,构造函数与一般函数没有任何区别,在创建实例时,如果我们使用了new关键字,那么这个函数就具有构造函数的特性,否则就是一般函数,如下所示,我们定义了一个Person类型:

    function Person(){
         this.name = 'xfrog';
         this.Say = function(){
              alert(this.name);
         };
    }

         当使用new关键字时,可以创建一个新的Person对象实例:

    var p1 = new Person();
    p1.Say();

         如果不使用new关键字,将直接执行Person函数,由于执行上下文为全局范围,故name属性和Say方法将被添加到window对象:

    Person();
    Say();
    window.Say();

    原型

         注意上述Person的定义方式,当使用new来创建Person实例时,将会执行Person构造函数,也就是会声明name属性和Say方法,这样可能产生效率问题,注意以下代码:

    var p1 = new Person();
         var p2 = new Person();
         var test = p1.Say == p2.Say;

         比较p1和p2两个Say函数指针,返回false,表示每个Person实例中的Say方法都是独立的,而事实上Say函数的功能是完全一样的,我们完全没有必要为每个对象重新分配Say函数”对象“,如果Person实例很多,将会造成大量的内存耗用。

         如果将Say函数提取出来放入全局执行范围,似乎可解决次问题:

    复制代码
    function Person(){
              this.name = 'xfrog';
              this.Say = say;     
         }
    
         function say(){
              alert(this.name);     
         }
    
         var p1 = new Person();
         var p2 = new Person();
         alert(p1.Say == p2.Say);
         p1.name = 'wang';
         p1.Say();
    复制代码

          由于this始终和执行上下文相关,p1和p2实例中的Say方法中会正确地返回对应实例的name属性。但是,使用此方式有违面向对象的思想,也失去了类型密封的原则。还会造成大量的全局函数。

         为了解决这些缺点,Javascript引出了原型的概念,简单理解,原型可以看成是类型的共享区,原型本身是一个对象,而对象中的属性对于类型来说是共享的。Javascript中每个类型通过prototype属性来表示原型,通过这个属性可指定共享方法:

    复制代码
    function Person(){
    
         }
         Person.prototype.name = 'xfrog';
         Person.prototype.Say = function(){
              alert(this.name);
         };
    
         var p1 = new Person();
         var p2 = new Person();
         alert(p1.Say == p2.Say);     //返回true
    复制代码

         为什么这里可以通过p1.Say来访问Say方法呢?这是因为ECMAScript标准规定了类型属性的查找顺序:先在类型的实例上查找,如果没有则继续在类型原型上查找,这一查找路径采用短路算法,即找到首个后即返回,考虑如下代码:

    复制代码
    function Person(){
              this.name = 'wang';
         }
    
         Person.prototype.name = 'xfrog';
         Person.prototype.Say = function(){
              alert(this.name);
         }
    
         var p1 = new Person();
         p1.Say();      //将返回wang
    复制代码

         上面提到prototype实际上是一个对象,那么我们是否可以直接访问呢? 在一些浏览器实现(如Chrome、Fixfox等)的确可通过实例的__proto__属性来访问内部的prototype对象,这种特征表明Javascript引擎在每个对象的内部都是通过一个变量来保存对prototype的引用,这保证了prototype对应整个类型的实例来说是共享的,例如,你可在Chrome浏览器内使用如下方式来访问Say方法:

    p1.__proto__["Say"]();

         由于原型是一个对象,我们可以直接将一个对象赋值给prototype:

    复制代码
    function Person(){
    
         }
    
         Person.prototype = {name:'xfrog', Say:function(){
              alert(this.name);
         }};
    复制代码

         注意这个方式下,实际上是完全替换了Person的prototype,这与上面Person.prototype.name方式还是有细微差异的,这是因为任何类型,Javascript引擎都会添加默认的prototype,在这个prototype中包含一个对构造函数的引用,即原型对象属性constructor,所以通常使用替代prototype方式时,我们需要手动加上constructor属性:

    复制代码
    Person.prototype = { 
              constructor: Person,
              name :'xfrog',
              Say:function(){
                   alert(this.name);
              }
         }
    复制代码

         注意,由于prototype对于整个类型是共享的,那么在prototype中的引用类型可能会存在问题,前面的Say函数作为一个对象,也是引用类型,所以每个实例中的Say都指向原型对象中的同一个函数,这本身没有问题,也是我们使用原型的初衷,但对于其他引用对象,可能结果并不是我们想要的:

    复制代码
    function Person(){
         }
    
         Person.prototype = {
              name: 'xfrog',
              obj : { age: 18 },
              Say : function(){
                   alert(this.obj.age);
              }
         };
    
         var p1 = new Person();
         var p2 = new Person();
         p1.obj.age = 20;
         p1.Say();
         p2.Say();
    复制代码

          p2.Say返回的是20,这是因为obj属性作为原型属性是共享的,在内存中只存在一个实例,所以通过p1修改后,p2只能得到修改后的状态。如果要避免此情况,可将obj属性放到实例中:

    function Person(){
              this.obj = { age: 18 };
         }
  • 相关阅读:
    Python 之 Django框架( Cookie和Session、Django中间件、AJAX、Django序列化)
    SAP 公司间开票 报错 :0080264464 000000 销售机构 未定义
    C++虚函数、纯虚函数,继承及多态、友元
    postgre 用户权限管理
    mysql 主从搭建
    vue 配置开发线上环境
    基于Ant Design UI框架的React项目
    postgresql数据库报“connections on Unix domain socket "/tmp/.s.PGSQL.5432"?”
    postgres 连接数查看与设置
    修改postgresql密码
  • 原文地址:https://www.cnblogs.com/sweeeper/p/5404182.html
Copyright © 2011-2022 走看看