javaScript高级特性(类比JAVA理解javaScript)

color=#0099ff size=72 face=”黑体”
本文承接于上一篇文章:javaScript基础 http://blog.csdn.net/wang2963973852/article/details/52945187
下一篇:javaScript自定义对象和继承的模式探究:http://blog.csdn.net/wang2963973852/article/details/53033123

本文需要阅读者对JAVA和javaScript有一定的基础:

在javaScript里面函数即是函数,同时也是对象(要牢牢记住这一点,理解透彻,对很多javaScript的语言特性就可以理解了)
因为javaScript里面的函数既是函数,同时也是对象,所以即表现出了函数的特性,也表示处理对象的特性(就像光的波粒二象性)
作为函数时的特性:
1、可以定义变量,并且作用域只在函数内
2、可以定义闭包函数
作为对象时的特性:
1、可以定义对象的属性
2、可以定义对象的方法
3、可以随时增删改属性和方法
(在JavaScript中,对象就是一个Map,里面只有键值对,除此之外,啥也没有!)
就是这两个特性让javaScript的函数变得强大和灵活(此时需要把函数和方法拆分出来看)
这里为什么要把变量和属性分开,还要把函数和方法分开:
首先:在这两种特性中,定义变量和属性的方式并不相同,其访问方式等也不相同
同样:在这两种特性中,定义函数和方法的形式不同,访问方式和用法也不相同

分析javaScript中创建对象的基本方式:

(1)对象字面量方式:其实就是JSON的格式:
var person = { name:”xiaogou”,age:27}
注意这种方式实际上只在内存中创建了一个person对象,也就是说如果你接下来将person的值再次赋值给另一个变量:
var student = person;
此时变量Student和person其实指向同一块内存地址(既:实际上只存在一个对象)

(2)还有一种是必须要使用new关键字的形式,也就是类似JAVA创建对象的方式:也就是构造函数的形式以及构造函数的变种形式
姑且称之为 new 调用,实际上,任何一个函数都可以使用这种调用,当调用时JavaScript引擎会创建一个对象返回。
这种和构造函数相关的形式:其实际上就是每new一下就创建一个对象,然后将对象的的引用赋值给变量,也就是说可以用来创建多个对象(并且可以用instanceof操作符来判断创建的对象的类型:例如:判断一个对象是否是Person(构造函数)的实例)
函数的原型方法:
(3)javaScript特有的原型方式:
javaScript中的函数也是一个对象,并且每个函数对象都有一个唯一的原型对象,(此处要理解构造函数对象和new出来的实例对象的不同的)因为原型对象是属于函数本身的,而不是属于创建出来的某一个对象的,因此函数的原型对象中定义的方法是所有根据构造函数new出来的对象的公共方法(即:每个对象都可以访问)

函数的原型方法是所有new出来的对象公有的方法,可能会有人想所有对象公有的方法不就是类似JAVA中的static方法吗?其实并不是:因为javaScript里面没有静态的概念,因此此处的公有方法和JAVA中的static方法还是有很大区别的:如下:

function Person(first,second){
  this.name= first;
  this.age = second;
}
Person.prototype.aaa = 123;
Person.prototype.run = function(){
  alert(this.age); //此处的this保证了可以访问到调用对象的属性
 this.age= "sb";
};
dog = new Person("dog",10);
cat = new Person("cat",30);

document.write(dog.run());//可以调用到原型对象中定义的run方法
document.write(dog.run());

首先:
此公有方法并不是说不能访问非静态的变量,在这里只要加上this就可以访问任何特定对象中的对应的属性值,因此在此方法里面修改属性只会修改对应的对象的属性值
其次,javaScript的函数的特点就和JAVA中的不一样:
javaScript中的方法是方法实体,方法名仅仅只是一个引用类型的变量,此变量仅仅保存着一个指针指向这个方法实体,因而javaScript中的方法是有被所有对象共享的潜质的(只要在不同的对象里面添加上此方法的引用指针,那么就可以通过指针调用到此方法)
但是JAVA中的方法就是方法,方法实体和方法名是绑定在一起的,JAVA中方法名不是变量而是调用此方法的唯一途径,因此方法从本质上来讲就没有共享的潜质,也就是说方法只能是也必须是某个对象的方法,不可能多个对象共享一个方法所以所有方法都是存在于某个对象里面的,(存在于实例对象或类对象里面)也因此某个对象里面的方法只能访问此对象里面的属性和方法,不能访问到其他对象里面的属性和方法,这就是JAVA中static方法,只能访问static属性的原因,因为static修饰的属性和方法是存在于类对象里面的,当然不能访问实例对象里面的属性

我们从其用途上来看,其实原型里面的方法和JAVA里面new出来一个对象然后,这个对象拥有的方法是一样的,
1、javaScript中的闭包和函数特性

var aa = function startCount(){   
      var count = 0;
      return function(a){
          return  count+=1; 
          }; 
     }();//立即执行startCount函数并将结果赋值给变量 aa
var bb = aa();

此段代码的意思是立即执行startCount函数并将结果赋值给变量 aa ,此时变量aa其实是持有一个函数对象引用的变量,然后以aa的名字加圆括号执行此引用变量指向的函数,并将结果赋值给bb
上面代码的执行结果说明:javaScript确实把函数作为对象来使用,而且可以吧函数作为返回值返回,并且返回的是函数对象,如果你将一个引用变量指向这个对象,那么你就可以直接使用引用变量名来调用这个函数

var aa = function startCount(){   
      var count = 0;
      function(a){//注意这里和上面代码段的区别
          return  count+=1; 
          }; 
     }();//立即执行startCount函数并将结果赋值给变量 aa
var bb = aa();

注意此段代码的注释,此段代码执行到var bb = aa();处时会出现错误undefined ,为什么呢,
因为上面代码立即执行startCount函数并将结果赋值给变量 aa,此时因为函数没有返回值,所以变量aa其实是没有任何值的,因此在你想要执行其执行的函数的时候会报错

注意javaScript中的函数的定义形式

var aa = function startCount(){   
      var count = 0;
      function(){//这种定义函数的形式是错误的
          return  count+=1; 
          }
     }();
var bb = aa();

注意此段代码和第一段正确的代码的区别:在startCount函数内部定义了一个没有名字和变量指向的函数,这种形式是错误的
上面的错误在于:在javaScript中函数必须要有一个名字,或者一个指向,否则此函数是无法被访问执行的要么是定义函数时直接赋予的名字例如:
function aa(){ ···}
要么是使用一个变量来承接这个函数例如:
var variable = function(){···}
而上面在startCount函数内部定义的函数,既没有自己的名字,也没有变量承接这个函数

2、闭包的另一个问题:内存泄漏
因为IE9之前的版本对javaScript对象和COM对象使用不同的垃圾回收策略(javaScript对象:标记清除、COM对象:引用计数),那么就可能出现的一种情况是:一旦一个闭包的作用域中保存着一个HTML元素,那么此HTML元素将永远无法被销毁,例如:

function myFunction(){
  var element = document.getElementById("demo");//此时element的类型是Object
  element.onclick = function(){
    alert(return element.id);
  }
  var id = element.id;
  alert(typeof id);//id的类型是String
  var click= element.onclick;//此时element.onclick的类型是function    
}

上面因为myFunction函数内部的一个闭包保存了一个HTML元素的引用,因此只要此匿名函数没有被销毁那么此HTML元素的引用数至少也是1,因此它所占的内存将一直不被回收(注意是HTML元素对象不会被回收,而HTML对象的回收是浏览器来执行的引用计数策略)

我们需要注意一个根本性问题:这种内存泄漏的问题的根本性原因是javaScrip对闭包的处理策略:即javaScript中的闭包会引用其包含函数(就是其外部函数)的整个活动对象(根据整个函数的变量和属性创建的一个对象),而这个独享中就包含了你在外部函数中创建的引用HTML元素的引用变量
知道了这点我们就可以找到解决方案:我们需要尽量避免闭包函数对对象的引用,方法就是得到我们需要的值之后,就把对象销毁,例如上面的闭包函数中我们需要的是element的ID,那么我们完全可以在使用一个变量将这个element对象的ID保存起来

function myFunction(){
  var element = document.getElementById("demo");//此时element的类型是Object
  var id = element.id;//保存element.id的副本
  element.onclick = function(){
    alert(id);//引用副本的值
  };
  element = null;//用完HTML元素的引用就立马将其销毁,这样就保证闭包中不会有指向HTML元素的引用
}

3、匿名函数可以模拟块级作用域(就是JAVA中的if、while、for等语句,特点是在块级作用域里面定义的变量在块级作用域外面无法访问)

javaScript中的作用域只有全局作用域和局部(函数)作用域两种,那么怎么创建出块级作用域呢?
创建方法:
javaScript中定义连续两个圆括号的函数是立即执行的函数,因此我们可以使用匿名函数的形式将一个块级作用域包裹起来,
因为javaScript里面函数执行完毕就会立即销毁里面定义的变量,因此就有了块级作用域的功能

function myFunction(){//在一个函数内部定义块级作用域
(function(){
    for(int i = 0;i<10;i++){ //注意此处错了,在javaScript里面定义变量不能用int ,只能用var
    alert(i);
    }
})();
}
(function (){
  for(vari = 0;i<10;i++){
    }
})();//注意此处为什么需要使用括号把函数括起来,因为如果不括起来javaScript会认为这是一个函数声明而不是表达式,只有括起来之后javaScript才能立即执行这个函数
alert(i); //此处不会正确显示i的值,因为上面的函数在执行完毕之后就销毁了,变量i也不存在了

这种技术经常用于限制向全局作用域里面添加过多的变量和函数,因为如果是一个大型的由很多人共同开发的项目,很有可能会出现全局变量名和函数名的重复问题,通过创建私有作用域每个人可以使用自己的变量,又不需要担心搞乱全局作用域

4、在javaScript中创建出类似JAVA类中的私有属性和公有方法:

function Person(value){//构造函数模型创建对象
    var name = value; //无法再函数外面直接访问,因为name是函数中定义的变量而不是person对象的属性
    this.getName = function(){
        return name;
    };
    this.setName = function(value){
        name = value;
    };
}

var person = new Person("小狗");//此时除了两个person对象的两个公有方法,我们没有任何途径可以直接访问到name变量
alert(person.getName());
alert(person.name); //报错undefined,因为person对象没有定义name属性

在javaScript里面函数不仅是函数,同时也是对象(要牢牢记住这一点,理解透彻,对很多javaScript的语言特性就可以理解了)
因为javaScript里面的函数既是函数,同时也是对象,所以即表现出了函数的特性,也表示处理对象的特性(就像光的波粒二象性)
函数的特性:
1、可以定义变量,并且作用域只在函数内
2、可以定义闭包函数
对象的特性:
1、可以定义对象的属性
2、可以定义对象的方法
3、可以随时定义属性和方法
注意这里为什么要把变量和属性分开,还要把函数和方法分开:
首先:在这两种特性中,定义变量和属性的方式并不相同,其访问方式等也不相同
同样:在这两种特性中,定义函数和方法的形式不同,访问方式和用法也不相同
因为有了对象的特性,我们可以通过特定的模式,来将函数打造成类似JAVA中的类的功能和特性(例如:继承)
因为有了函数的特性,我们可以有更灵活的使用场景

5、模块模式:(javaScript中用于为单例创建私有属性和特权方法(公有方法)的模式)

创建方法:
创建一个立即执行的匿名函数,然后使用对象字面量的方式创建出其返回值对象,并将这个对象赋值给一个变量,
那个这个变量实际上就是一个单例对象,并且因为对象是在匿名函数内部创建的因此可以访问函数内部定义的私有变量和方法,
而对象自己定义的是可以被外界访问的属性和方法,这样就完成了为单利创建私有属性和公有方法的目标

var singleton = function(){ //单例的关键在于这里:立即执行并赋值给变量,因为使用对象字面量的方式创建对象的过程只执行了一次,因此只存在一个对象
    var privateVariable = 10;//私有的变量
    functionn privateMothod(){//私有的函数
        alert("hellow World");
    }
    return { // 返回一个使用对象字面量的方式创建出来的对象,因为javaScript里面作用域只有全局和函数,所以此对象可以访问外部函数中的属性和方法
        publicProperty:12;
        publicMethod:function(){
            privateVariable+=1,
            privateMothod();
            }
        }
}();

上面的代码是立即执行一个匿名函数,然后将一个对象赋值给了singleton变量,因此这个singleton变量就是一个单例对象,按照JAVA中的单例类来说:此对象有着只能在本类中访问的私有的方法和属性,以及可以通过对象.属性或者对象.方法名来访问公有方法

这种单例模式创建出来的每一个单例都是Object的实例,因为我们是通过对象字面量的方式创建的对象,我们常常用一个单例来管理应用程序级别的信息

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值