zoukankan      html  css  js  c++  java
  • 关于delete和对象复制

    本码农的惯例,开篇废话几句...

    前天小生又被虐了...

    没办法,作为一个资深code user,我用代码的能力,解决问题的能力自问是不弱的...

    但是自身的前端基础说实话还是不过硬,最明显的表现就是,对于JS核心的研究做得比较少。

    另外就是概念方面很脆弱(PS:我的习惯是用通俗的比喻来理解技术,那些乏味的概念实在记不住几个)

    现实依旧一如既往的残酷,许多平时用的很少的知识点经常被用作体现一个人能力水平的指标。

    其实也是合理的,毕竟用的少就不去研究,这是说不过去的。

    OK,废话完毕,开始正题。

    关于 delete

    下面会列出许多例子,如果你要用开发者工具来调试,在测试不同例子的时候麻烦刷新一下页面,否则可能会由于用了同样的变量而出现问题。

    首先来个简单的例子(其实在此之前我在工作中完全没用过 delete,所以一旦被人问到这个,十有八九是要跪了的...)

    var x = {a: 1};
    delete x.a; // true
    x.a; // undefined (删除对象属性成功)

    然后再试试删除变量

    var x = 1;
    delete x; // false;
    x; // 1 (删除变量失败了)

    再试试删除一个函数看看...

    //先试试这种写法,直接声明一个函数
    function x() {}; 
    delete x; // false;
    typeof x; // function (删除函数失败)
    
    //再试试这种写法,声明一个变量,并将一个匿名函数赋值给它
    var y = function () {};
    delete y; // false;
    typeof y; // function (删除函数失败)

    下面,让我来毁你的三观...

    z = function () {};
    delete z; // true
    typeof z;// undefined (删除成功了...)

    为什么这次能够删除了呢?假如都是在全局上下文下面执行,那他们就都是 window 对象的属性,为什么上面的例子又删除成功了呢?

    原因是上面的例子,实际上相当于执行了下面的代码

    window.z = function () {}; // 对属性赋值,而不是定义
    delete z; // true
    typeof z;// undefined (删除成功了...)

    假如一个变量没有用 var 进行声明,那么 JS 会将该变量变成 window 对象的属性,然后对属性进行赋值。

    由于这个过程对开发者而言是不可见的,因此称它为隐式属性赋值。当然你也可以显式的对window对象添加一个属性并对其赋值,结果是一样的。

    这种显式/隐式赋值的属性,是可以用 delete 删除的。

    上面的例子都是在全局环境下的,下面我们将战场转移到局部环境,匿名函数的内部。

    (function () {
      this.x = 1; // 显式属性赋值
      var y = 2; // 声明一个局部变量
      z = 3; // 隐式属性赋值,变成window对象的一个属性
    
      delete this.x; // true
      delete x; // false
      delete z; // true
    
      console.info(this.x); // undefined (删除成功)
      console.info(y); // 2 (删除失败)
      console.info(window.z); // undefined (删除成功)
    })()

    可以看到,结果全局环境下是差不多的。

    我们把环境转移到更残酷的战场 —— eval

    //温馨提示,测试新一段代码前注意先刷新下页面
    eval('function x(){}');
    typeof x; // function
    delete x; // true;
    typeof x; // undefined (删除成功)

    然后我又来毁你三观了...

    //老规矩,测试新一段代码前注意先刷新下页面,后面就不再提示了...
    var x = 1;
    eval('function x(){}');
    typeof x; // function
    delete x; // false;
    typeof x; // function(删除失败!)

    为什么会这样?甚至你再看看这个例子

    //在 eval 后面声明一个同名变量,也删除失败
    eval('function x(){}');
    var x = 1;
    typeof x; // function
    delete x; // false;
    typeof x; // function(删除失败!)

    首先,这里明确一点就是,eval 里的代码声明的变量或者函数,是可以用 delete 删除的。

    eval 里的代码声明的变量或者函数,如果在外部已经被声明了(不单单是 eval 的上文,还包括 eval 的下文),则会对该变量重新进行赋值,因为 eval 里代码的调用,永远在其外部上下文中其他变量和函数声明之后。

    这样一来,该变量就是已经存在的,而非执行 eval 传入的代码声明的了。

    所以上面的例子之所以删除失败,不是因为 eval 里代码声明的变量删除不了,而是那个变量压根就不是 eval 里的代码声明的。

    OK,最后来一份比浅层总结(之所以说是浅层总结,是因为其实还有一些情况这里是没列出的,但那太过深入了,如果你有兴趣可以研究,但如非必要,我觉得还是算了吧...记住那些东西实在是累人...)

    1.显示/隐式的对象属性赋值,该属性都是可以通过 delete 删除的

    2.通常情况下,代码中声明的变量和函数不能通过 delete 删除

    3.通过 eval 函数传入代码声明的变量和函数可以通过 delete 删除,前提是在此之前,该变量不曾在外部上下文中被声明

    最后,如果你想对delete作更深入的研究,想揪其原理,这里有一篇很好很专业的文章:http://justjavac.com/javascript/2013/04/04/understanding-delete-in-javascript.html

    关于对象复制

    由于对象是引用类型的,所以当我们响应克隆一个对象的时候,不能用常规的赋值来完成。

    // 下面的代码,实际上只是让两个不同的变量引用了同一个对象
    var a = {
      name : 'Jack',
      sex : 'male'
    };
    var b = a;

    假如要将对象复制,正确的做法应该是这样

    var a = {
      name : 'Jack',
      sex : 'male'
    };
    var b = {};
    
    for (var key in a) {
      b[key] = a[key];
    }

    但...这样就够了么?万一对象的某个属性是另一个对象,或者是一个数组呢?

    这样一来,就涉及到对象的浅复制和深复制了。

    上面的做法,只能实现对象的浅复制,对于深复制,我们要用下面的方法来完成。

    // 声明一个对象克隆函数
    function clone (obj) {
      var result = {};
    
      var _clone = function (objO, objC) {
        for (var key in objO) {
          var curProperty = objO[key];
    
          if (curProperty.constructor == Array) {
            // 属性的值为数组时...
            objC[key] = [];
            for (var i = 0, len = curProperty.length; i < len; i++) {
              objC[key].push(curProperty[i]);
            }
          } else if (curProperty.constructor == Object) {
            // 属性的值为对象时...
            objC[key] = {};
            _clone(curProperty, objC[key]);
          } else {
            // 其他情况...
            objC[key] = objO[key];
          }
    
        }
      };
    
      _clone(obj, result);
    
      return result;
    }
    
    // 测试...
    var a = {
      name : 'Jack',
      love : ['Lily', 'Kate', 'Lucy'],
      fav : {
        color : ['red', 'black'],
        sport : 'football'
      },
      eat : function () {
        alert('fuck you!')
      }
    };
    
    var b = clone(a);
  • 相关阅读:
    时间序列模型文章收集
    因果推断文章收集
    Git常用命令
    redis配置
    团队作业2:需求分析&原型设计
    团队项目作业1-团队展示与选题
    结对编程1-模块化
    个人作业2:APP案例分析
    为农三载
    面试题随记一
  • 原文地址:https://www.cnblogs.com/czf-zone/p/4375781.html
Copyright © 2011-2022 走看看