美文网首页
深入理解JS

深入理解JS

作者: hutou | 来源:发表于2016-06-08 14:35 被阅读273次

    说明

    这篇文章将包含如下的几个内容:

    1. 对象的理解
    1. 函数的理解
    2. 封装与继承
    3. 常见的问题

    一. 对象的理解

    1. 字面量
      通过字面量可以很容易的定义对象。在大括号中直接定义,属性:值
    var o = {
        x : 1 ,
        y : 2 ,
        method : function() {
            console.info("this is a method");
        }
     }
    
    1. 对象的检索
      可以通过属性名直接得到属性值,也可以使用[]来获得
    console.info(o.x);
    console.info(o["y"]);
    
    1. 对象的更新
      可以直接对对象的属性进行设置操作,更新对象属性;可以直接添加属性,并赋值
    o.x = 10
    console.info(o.x);  // 10
    o.z = 20
    console.info(o.z);  //  20
    
    1. 对象的枚举
      可以通过for..in来进行枚举;方法也会被枚举出来
    for (name in o) {
        if (o.hasOwnProperty(name)) {
            console.info("property = " + name + " and value = " + o[name]);
        }
    }
    

    这里注意:为什么有一个hasOwnProperty,是为了防止枚举出原型链上的属性。下面举例说明

    Object.prototype.basex = 12
    var o = {
        x : 100
    }
    // 这里会打印x
    for (name in o) {
        if (o.hasOwnProperty(name)) {
            console.info(name);
        }
    }
    // 这里会打印x, basex
    for (name in o) {
        console.info(name);
    }
    

    如果hasOwnProperty被改写,可以使用如下方法进行调用

    var o = {
        x : 100,
        // 被改写
        hasOwnProperty : 111
    }
    for (name in o) {
        // 改变调用方法
        if (Object.prototype.hasOwnProperty.call(o,name)) {
            console.info(name);
        }
    }
    
    1. 删除对象属性
    delete o.x
    
    1. 原型链
      对象可以使用原型链上的属性
    Object.prototype.x = 100
    var o = {}
    console.info(o.x);  //  100
    delete o.x
    //  不能删除原型链的属性
    console.info(o.x);  //  100
    //  自己定义属性
    o.x = 200
    //  显示自己定义的属性,覆盖原型链属性
    console.info(o.x);  //  200
    delete o.x
    //  删除自己定义的属性,漏出原型链属性
    console.info(o.x);  //  100
    

    二. 函数的理解

    函数是JS的一等公民,是JS中最灵活的部分,也是最难理解的部分

    1. 函数对象
      函数也是对象,也可以存在属性和方法
    var F = function() {    
    }
    //  使用对象一样使用函数
    F.x = 12;
    console.info(F.x); // 12
    
    1. 函数字面量
      函数的字面量包含四个部分:保留字function;函数名(可以省略);函数参数,以逗号分隔;花括号(函数主体)。
      函数的字面量可以出现在任何允许出现表达式的地方
    2. 函数的调用
      在进行函数调用的时候,会隐含的传入this和arguments。其中arguments是一个伪数组,表示传入的参数。this可以理解为调用的上下文。函数有几种调用方式,下面将分别说明
      作为方法调用
    var o = {
        x : 100,
        method : function() {
            //  方法中的调用,this表示方法的对象,这里是o
            console.info(this.x);
        }
    }
    o.method()  //  100
    

    函数调用

    function f(x) {
        // this指代全局变量
        this.x = x
        console.info(x);
    }
    try{
        console.info(x);    // not defined  
    }catch(e){
        console.info(e);    // not defined error
    }
    // 调用函数
    f(100)
    console.info(x);    //  100
    

    构造器调用,将函数作为一个构造器

    //  构造器
    var F = function(x) {
        this.x = x
    }
    //  构造器中定义方法
    F.prototype.read = function() {
        return this.x
    }
    //  构造一个对象
    var ins = new F(200)
    //  调用方法(this指代对象本身)
    console.info(ins.read());   //  200
    console.info(ins.x);        //  200
    

    call apply
    在之前的对象说明中,我们已经展示了使用call调用方法的例子。这种调用方法,会主动改变上下文,也就是说this会由我们设定

    //  定义一个对象
    var o = {
        x : 100
    }
    //  定义一个函数
    var f = function() {
        //  使用this
        console.info(this.x);
    }
    //  两种调用方法的区别
    f()         //  undefined
    f.call(o)   //  100
    
    1. 高阶函数
      当一个函数接受另一个函数作为参数的时候,就是高阶函数。我们可以自定义高阶函数,下面是两个使用Array中两个高阶函数的例子
      Array.map
    //  定义一个数值
    var arr = [1,2,3,4,5,6]
    //  通过高阶函数运算
    var arrD = arr.map(function(x) {
        return x*x
    })
    console.info(arrD); //  [ 1, 4, 9, 16, 25, 36 ]
    

    Array.reduce

    //  定义一个数值
    var arr = [1,2,3,4,5,6]
    //  通过高阶函数进行求和运算
    var d = arr.reduce(function(x,y) {
        return x+y
    })
    console.info(d);    //  21
    
    1. 闭包
      内层函数可以使用外层函数的特点叫做闭包。下面会讲述使用闭包解决一些常见的错误

    三. 封装与继承

    javascript是面向对象的,但是没有语法层面的继承和封装语法。

    1. 定义类
      js中我们可以有类似java的使用方法:定义对象,通过new实例化一个对象
     // 定义一个类
     var F = function() {}
     // 定义类中的方法
     F.prototype.test = function(o) {
        console.info(o);
     }
     F.prototype.display = function() {
        console.info("this is a display function");
     }
     // 实例化一个类
     var ins = new F;
     ins.test('lxm')
     ins.display()
    
    1. 封装
      通过字面量定义的对象是无法保护属性的,可以随意的修改和增减。下面介绍一个数据封装的方法
    //  创建构造器
    var F = function() {
        //  私有属性
        var x;
        //  this是必须的
        this.getX = function() {
            return x;
        }
        this.setX = function(x1) {
            x = x1;
        }
    }
    //  通过构造器创建对象
    var instance = new F
    //  使用对象
    instance.setX(100)
    console.info(instance.x);       //  undefined
    console.info(instance.getX());  //  100
    
    1. 继承
    //  创建一个继承函数
    if(typeof Object.inhert !== 'function'){
        Object.inhert = function(o) {
            var F = function() {}
            F.prototype = o
            return new F
        }
    }
    //  父对象
    var o = {
        x : 1,
        y : 100
    }
    //  创建子对象
    var child = Object.inhert(o)
    console.info(child.x);  //  1
    console.info(o.x);      //  1
    //  设置x
    child.x = 200
    console.info(child.x);  //  200
    console.info(o.x);      //  1
    

    四. 常见的问题

    整理一下在实际工作中经常发生的误用

    1. 全局作用域
          myglobal = "hello"; // 不推荐写法
          console.log(myglobal); // "hello"
          console.log(window.myglobal); // "hello"
          console.log(window["myglobal"]); // "hello"
          console.log(this.myglobal); // "hello"  
    

    使用显示声明的变量不能被delete删除

    var sss = 'abc';
    ddd = 'abc';
    delete sss; // false
    delete ddd; // true
    
    1. 函数内使用var避免产生全局变量
        function abc(a,b){
          ret = a + b;
          return ret;
        }  
    
        console.info(abc(1,2));
        console.info(this.ret);
    
    1. 定义变量产生的全局变量
        function abc(a,b){
          var ret = a + b;
          var x = y = 3;
          return ret;
        }  
    
        console.info(abc(1,2));
        console.info(this.ret);        
        console.info(y);
    
    1. 变量提升
        function abc(){
          alert(myname);
          var mynam ='lxm';
          alert(myname);
        }
    
    第一阶段是变量,函数声明,以及正常格式的参数创建,这是一个解析和进入上下文 的阶段。
    第二个阶段是代码执行,函数表达式和不合格的标识符(为声明的变量)被创建
    
    1. 自动添加分行
        function abc(){
          var s = 12;
          return
          s
        }
    
        abc();        
    
    1. for循环中产生全局变量
        for(var i=0;i < 12;i++){
        }
    
        console.info("gloabl i = " + i);
    

    注意缓存数组的长度,尤其是进行dom操作的时候

        for(var i=0, max=document.getElementsByName().length;i<max;i++){
        }
    
    1. 循环中常见的错误
        var funArr = [];
        for(var i=0;i<10;i++){
          funArr[i] = function(){
            console.info(i);
          }
        }
        funArr[2]();
    

    通过闭包来解决

        var funArr = [];
        for(var i=0;i<10;i++){
          (function(i){
            funArr[i] = function(){
              console.info(i);
            }
          })(i);
        }
        funArr[2]();
    
    1. 使用for..in进行属性遍历
      进行枚举,但是不保证顺序;通常需要过滤原型链上的属性
        // 对象
        var man = {
           hands: 2,
           legs: 2,
           heads: 1
        };
        for(var t in man){
          if(man.hasOwnProperty(t))
            console.info(t);
        }        
    

    另外的一种调用方式,可以避免对象将hasOwnProperty重新定义

        for (var i in man) {
           if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤
              console.log(i, ":", man[i]);
           }
        }
    
    1. 关于扩展内置属性的问题
      由于有了prototype的存在,可以很方便的进行属性的扩充
      最好不增加内置原型
        Object.prototype.testMethod = function(){
          //console.info('这是我弄的!');
          return "这是我弄的";
        }
    
        var sss = {};
        sss.testMethod();
    
    1. 尽可能避免隐式类型转换
      使用 === !== 来进行判断
    2. eval尽可能不要用
      这个处理可以接收任何字符串,当成js代码来执行
    3. 关于类型转换
      parseInt尽可能少用,使用Number()来替换
    4. 内嵌方法中的this错误
      通常我们在Java等面向对象的语音中都见过this,指代当前的对象,在编译期间就已经确定下来,是编译期绑定的。而javascript是解释性语言,this是动态的,是运行期绑定的,这就导致了this关键字具有多重含义,不好理解。下面是一个js设计中的问题
    var x = 'global'
    var o = {
        x : 'property',
        dis : function() {
            var x = 'local'
            console.info("dis x = " + this.x);          //  property
            //  定义一个内嵌方法
            var local = function() {
                console.info("local x = " + this.x);    //  global
            }
            //  执行内嵌方法
            local()
        }
    }
    o.dis()
    

    这个测试结果是在浏览器中执行,如果在Node.js环境下,会有点不同

    相关文章

      网友评论

          本文标题:深入理解JS

          本文链接:https://www.haomeiwen.com/subject/vuihdttx.html