关于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);
时间: 2024-08-23 19:52:18

关于delete和对象复制的相关文章

C++对象复制控制

C++中对象的复制通过一系列的构造函数或者赋值函数来实现的,包括copy construstor,move construstor,copy assignment operator,move assignment operator,实现过程中还可能有destructor的参与,一般情况下,这些函数编译器会为我们自动合成,但还有很多时候,编译器会将他们合成为delete,因此需要我们自己编写.也有一些时候,我们需要禁止或者强制编译器为我们合成这些函数. 首先看一下什么情况下编译器不会为我们自动合成

对象复制、克隆、深度clone

-------------------------------------------------------------------------------- ------------------------------------------------- 知道Java对对象和基本的数据类型的处理是不一样的.和C语言一样,当把Java的基本数据类型(如int,char,double等)作为入口参数传给函数体的时候,传入的参数在函数体内部变成了局部变量,这个局部变量是输入参数的一个拷贝,所有的

C++对象模型——对象复制语意学 (Object Copy Semantics)(第五章)

5.3    对象复制语意学 (Object Copy Semantics) 当设计一个 class,并以一个 class object指定给 class object时,有三种选择: 1.什么都不做,因此得以实施默认行为. 2.提供一个 explicit copy assignment operator. 3.明确地拒绝一个 class object指定给另一个 class object. 如果要选择第3点,不允许将一个 class object指定给另一个 class object,那么只要

PHP5的对象复制

今天用yii开发程序,一个bug改了一晚上,最后发现问题出在了对象复制机制上,PHP5之前的对象复制只需要$object_a = $object_b即可,但PHP5这样得到的是浅复制,及指针指向,并不是真正的数据复制,如改变$object_b的值,则$object_a也同样会发生变化,这时,需要令$object_a = clone $object_b,这样$object_a就不会跟随变化了. 但有一个问题,clone同样是PHP5才有的,因此,如果设计对象复制,则PHP5为分界版本,兼容问题需要

【转】JavaScript中的对象复制(Object Clone)

JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b.k2 = 4; 如果只想改变b而保持a不变,就需要对对象a进行复制. 用jQuery进行对象复制 在可以使用jQuery的情况下,jQuery自带的extend方法可以用来实现对象的复制. a = {k1:1, k2:2, k3:3}; b = {}; $.extend(b,a); 自定义clone

笔记——JS的对象复制

JS的对象复制-- Js的Number类型和String类型都不是地址引用,而是重新创建对象复制值:var a = 1;var b = a;b = 2;alert(a);结果:1 var c = "abc";var d = c;d = "def";alert(c);结果:abc 只有对象类型才是地址引用的:var a = {x:1};var b = a;b.x = 2;alert(a.x);结果:2 所以复制对象不要用"=",而是遍历对象复制对象

通过反射克隆对象,对象复制(克隆)工具类

最近做的项目中,经常会遇到用视图来操作数据库的,但是页面需要的则是某个实体对象,在controller层查出list<view> 还要把将view对象转化成entity对象.需要写一大堆的get和set方法,而且如果实体增删字段的话,还需要把转化代码再修改一下,让人头疼. 当我需要操作一个实体对象完成两件不同的事情,这2个方法中会修改实体对象中的属性,第一个方法调用后,再调用第二个方法时,会受影响.为了保证不受影响,必须copy一份属性值一模一样的实体.这时候就需要一个工具类来完成了. 本着磨

Java反射 - 2(对象复制,父类域)

为什么要复制对象?假设有个类Car,包含name,color2个属性,那么将car1对象复制给car2对象,只需要car2.setName(car1.getName)与car2.setColor(car1.getColor)两部操作即可. 实际项目中有很多类都有超过几十个,甚至上百个字段,这时如果采用以上方式,将会有很大的代码工作量与重复劳动.解决方法是使用反射机制. 首先有2个类如下 1 /** 2 * Created by yesiming on 16-11-19. 3 */ 4 publi

通过反射克隆对象,对象复制(克隆),对象合并工具类 升级版

先上代码,有时间再详说: package com.kaiyuan.common.util; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 通用对象co