一、问题的提出

  我们先来看看下面几段代码,要注意的是,以下代码不要在浏览器的开发者工具(如FireBug、Chrome Developer tool)中运行,原因后面会说明:

  为什么我们可以删除对象的属性:

var o = { x:  };
delete o.x; // true
o.x; // undefined  

但不以删除像这样声明的变量:

var x = ;
delete x; // false
x; // 1

也不能删除像这样定义的函数:

function x(){}
delete x; // false
typeof x; // "function"

注意:当delete操作符返回true时表示可以删除,返回false表示不能删除

  要理解这一点,我们首先需要掌握像变量实例化和属性特性这样的概念--遗憾的是这些内容在一些javascript的书中很少讲到。理解它们并不难,如果你不在乎它们为什么这么运行,你可以随意的跳过这一部分。

二、代码类型

  在ECMAScript中有三种类型的可执行代码:Global code(全局代码)、Function code(函数代码)和 Eval code(放在Eval中执行的代码)。

var x=;//Global code
function test(){
var y=;//Function Code
eval("var z=3");//Eval Code in Function
}
eval("function evalTest(){}");//Eval Code in Global

三、执行上下文

  当ECMAScript 代码执行时,它总是在一定的上下文中运行,执行上下文是一个有点抽象的实体,它有助于我们理解作用域和变量实例化如何工作的。对于三种类型的可执行代码,每个都有执行的上下文。当一个函数执行时,可以说控制进入到函数代码(Function code)的执行上下文。全局代码执行时,进入到全局代码(Global code)的执行上下文。

  正如你所见,执行上下文逻辑上来自一个栈。首先可能是有自己作用域的全局代码,代码中可能调用一个函数,它有自己的作用域,函数可以调用另外一个函数,等等。即使函数递归地调用它自身,每一次调用都进入一个新的执行上下文。

四、Activation object(激活对象)/Variable object(变量对象)

  每一个执行上下文在其内部都有一个Variable Object。与执行上下文类似,Variable object是一个抽象的实体,用来描述变量实例化的机制。有趣的是在代码中声明的变量和函数实际上被当作这个变量对象的属性被添加。

  当进入全局代码的执行上下文时,一个全局对象用作变量对象。这也正是为什么在全局范围中声明的变量或者函数变成了全局对象的属性。

/* remember that `this` refers to global object when in global scope */
var GLOBAL_OBJECT = this; var foo = ;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo; // true function bar(){}
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true

全局变量变成了全局对象的属性,但是,那些在函数代码(Function code)中定义的局部变量又会如何呢?行为其实很相似:它成了变量对象的属性。唯一的差别在于在函数代码(Function code)中,变量对象不是全局对象,而是所谓的激活对象(Activation object)。每次函数代码(Function code)进入执行作用域时,就会创建一个激活对象(Activation object)。

  不仅函数代码(Function code)中的变量和函数成为激活对象的属性,而且函数的每一个参数(与形参相对应的名称)和一个特定Arguments 对象也是。注意,激活对象是一种内部机制,不会被程序代码真正访问到。

(function(foo){ 

var bar = ;
function baz(){} /*
In abstract terms, Special `arguments` object becomes a property of containing function's Activation object:
ACTIVATION_OBJECT.arguments; // Arguments object ...as well as argument `foo`:
ACTIVATION_OBJECT.foo; // 1 ...as well as variable `bar`:
ACTIVATION_OBJECT.bar; // 2 ...as well as function declared locally:
typeof ACTIVATION_OBJECT.baz; // "function"
*/ })();

最后,在Eval 代码(Eval code)中声明的变量作为正在调用的上下文的变量对象的属性被创建。Eval 代码(Eval code)只使用它正在被调用的哪个执行上下文的变量对象。

var GLOBAL_OBJECT = this; 

/* `foo` is created as a property of calling context Variable object,
which in this case is a Global object */ eval('var foo = 1;');
GLOBAL_OBJECT.foo; // 1 (function(){ /* `bar` is created as a property of calling context Variable object,
which in this case is an Activation object of containing function */ eval('var bar = 1;'); /*
In abstract terms,
ACTIVATION_OBJECT.bar; // 1
*/ })();

五、属性特性 
  现在变量会怎样已经很清楚(它们成为属性),剩下唯一的需要理解的概念是属性特性。每个属性都有来自下列一组属性中的零个或多个特性--ReadOnly, DontEnum, DontDelete 和Internal,你可以认为它们是一个标记,一个属性可有可无的特性。为了今天讨论的目的,我们只关心DontDelete 特性。

  当声明的变量和函数成为一个变量对象的属性时--要么是激活对象(Function code),要么是全局对象(Global code),这些创建的属性带有DontDelete 特性。但是,任何明确的(或隐含的)创建的属性不具有DontDelete 特性。这就是我们为什么一些属性能删除,一些不能。

var GLOBAL_OBJECT = this; 

/* `foo` is a property of a Global object.
It is created via variable declaration and so has DontDelete attribute.
This is why it can not be deleted. */ var foo = ;
delete foo; // false
typeof foo; // "number" /* `bar` is a property of a Global object.
It is created via function declaration and so has DontDelete attribute.
This is why it can not be deleted either. */ function bar(){}
delete bar; // false
typeof bar; // "function" /* `baz` is also a property of a Global object.
However, it is created via property assignment and so has no DontDelete attribute.
This is why it can be deleted. */ GLOBAL_OBJECT.baz = 'blah';
delete GLOBAL_OBJECT.baz; // true
typeof GLOBAL_OBJECT.baz; // "undefined"

六、内置属性和DontDelete 

  一句话:属性中一个独特的特性(DontDelete)控制着这个属性是否能被删除。注意,对象的内置属性(即对象的预定义属性)有DontDelete 特性,因此不能被删除。特定的Arguments 变量(或者,正如我们现在了解的,激活对象的属性),任何函数实例的length属性也拥有DontDelete 特性。

(function(){ 

/* can't delete `arguments`, since it has DontDelete */ 

delete arguments; // false
typeof arguments; // "object" /* can't delete function's `length`; it also has DontDelete */ function f(){}
delete f.length; // false
typeof f.length; // "number" })();

与函数参数相对应的创建的属性也有DontDelete 特性,因此也不能被删除。

(function(foo, bar){ 

delete foo; // false
foo; // 1 delete bar; // false
bar; // 'blah' })(, 'blah');

七、未声明的赋值

  简单地就是未声明的赋值在一个全局对象上创建一个可删除的属性。

var GLOBAL_OBJECT = this; 

/* create global property via variable declaration; property has DontDelete */
var foo = ; /* create global property via undeclared assignment; property has no DontDelete */
bar = ;//可理解为 window.bar=2; 根据上面的第五点是可以删除的 delete foo; // false
typeof foo; // "number" delete bar; // true
typeof bar; // "undefined"

请注意,DontDelete特性是在属性创建的过程中确定的,后来的赋值不会修改现有属性已经存在的特性,理解这一点很重要。

/* `foo` is created as a property with DontDelete */
function foo(){} /* Later assignments do not modify attributes. DontDelete is still there! */
foo = ;
delete foo; // false
typeof foo; // "number" /* But assigning to a property that doesn't exist,
creates that property with empty attributes (and so without DontDelete) */ this.bar = ;
delete bar; // true
typeof bar; // "undefined"

八、Eval code 
  在Eval中创建的变量或方法比较特别,没有DontDelete特性,也就是说可以删除。

eval("var x = 1;");
console.log(x); // 1
delete x;
console.log(typeof x); // undefined eval("function test(){ var x=1; console.log(delete x);/* false */;return 1;}");
console.log(test()); // 1
delete test;
console.log(typeof test); // undefined  

注意,这里说的在Eval中创建的变量或方法不包括方法内部的变量或方法,如上面代码中的红色部分,仍然跟之前讲的一致:不能被删除。

九、FireBug的困惑 

  我们看一段在FireBug中执行的代码结果:

var x=;
delete x;
console.log(typeof x);//undefined function y(){
var z=;
console.log(delete z);//false
}
y();
delete y;
console.log(typeof y);//undefined

这明明是违反上述规则的,但跟上面第八点对比后发现,这正在代码在eval中执行的效果。虽然没有证实,但我猜测FireBug(Chrome Developer tool)中控制台代码是用eval执行的。

所以,当大家在测试JS代码时,如果涉及到当前上下文环境时特别要注意。

十、delete操作符删除的对象

  C++中也有delete操作符,它删除的是指针所指向的对象。例如:

class Object {
public:
Object *x;
} Object o;
o.x = new Object();
delete o.x; // 上一行new的Object对象将被释放

但Javascript的delete与C++不同,它不会删除o.x指向的对象,而是删除o.x属性本身。

var o = {};
o.x = new Object();
delete o.x; // 上一行new的Object对象依然存在
o.x; // undefined,o的名为x的属性被删除了 

在实际的Javascript中,delete o.x之后,Object对象会由于失去了引用而被垃圾回收, 所以delete o.x也就“相当于”删除了o.x所指向的对象,但这个动作并不是ECMAScript标准, 也就是说,即使某个实现完全不删除Object对象,也不算是违反ECMAScript标准。

  “删除属性而不是删除对象”这一点,可以通过以下的代码来确认。

var o = {};
var a = { x: };
o.a = a;
delete o.a; // o.a属性被删除
o.a; // undefined
a.x; // 10, 因为{ x: 10 } 对象依然被 a 引用,所以不会被回收

另外,delete o.x 也可以写作 delete o["x"],两者效果相同。

十一、其他不能被删除的属性

  除了上面说过的内置属性(即预定义属性)不能被删除外,prototype中声明的属性也不能delete:

function C() { this.x = ; }
C.prototype.x = ;
C.prototype.y = ; var o = new C();
o.x; // 42, 构造函数中定义的o.x delete o.x; //true 删除的是自身定义的x
o.x; // 12, prototype中定义的o.x,即使再次执行delete o.x也不会被删除 delete o.y; //true,因为 o自身没有o.y属性,y存在于prototype链中,也就是说对象自身属性和prototype属性是不同的
o.y; //13

小结

  上面说了那么多,希望对大家认识JavaScript中的Delete有所帮助。由于水平有限,不保证完全正确,如果发现错误欢迎指正。

原文:1、http://perfectionkills.com/understanding-delete/(英文) 
  2、http://nanto.asablo.jp/blog/2008/01/09/2552470(日文)

本文首发http://jscode.cnblogs.com

JavaScript delete用法,属性,特性,执行上下文,激活对象 综合篇的更多相关文章

  1. Javascript本质第二篇:执行上下文

    在上一篇文章<Javascript本质第一篇:核心概念>中,对Javascript执行上下文做了解释,但是这些都是基于Javascript标准中对执行上下文的定义,也就是说理论上的东西,本 ...

  2. Javascript笔记:作用域和执行上下文

    一.作用域 Javascript的作用域规则是在编译阶段确定的,有声明时的位置决定. JS中有全局作用域,函数作用域,块级作用域(ES6引入). 1. 全局作用域 在整个程序生命周期内都是有效的,在任 ...

  3. JavaScript内部原理系列-执行上下文(Execution Context)

    概要 本文将向大家介绍ECMAScript的执行上下文以及相关的可执行代码类型. 定义 每当控制器到达ECMAScript可执行代码的时候,控制器就进入了一个执行上下文.执行上下文(简称:EC)是个抽 ...

  4. javascript执行上下文学习一

    原文: http://web.jobbole.com/84044/ http://blog.csdn.net/github_34514750/article/details/52901781 1.三种 ...

  5. javascript 执行上下文的理解

    首先,为什么某些函数以及变量在没有被声明以前就可以被使用,javascript引擎内部在执行代码以前到底做了些什么?这里,想信大家都会想到,变量声明提前这个概念: 但是,以下我要讲的是,声明提前的这个 ...

  6. JavaScript学习系列之执行上下文与变量对象篇

    一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变 ...

  7. JavaScript的执行上下文,真没你想的那么难

    作者:小土豆 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/2436173500265335 前言 在正文开始前,先来看 ...

  8. Js 作用域与作用域链与执行上下文不得不说的故事 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄

    最近在研究Js,发现自己对作用域,作用域链,活动对象这几个概念,理解得不是很清楚,所以拜读了@田小计划大神的博客与其他文章,受益匪浅,写这篇随笔算是自己的读书笔记吧~. 作用域 首先明确一个概念,js ...

  9. 通俗易懂的来讲讲js的函数执行上下文

    0.开场白 在平时编写JavaScript代码时,我们并不会和执行上下文直接接触,但是想要彻底搞懂JavaScript函数的话,执行上下文是我们绕不过去的一个知识点. 1.执行上下文栈 JavaScr ...

随机推荐

  1. 64位系统下,一个32位的程序究竟可以申请到多少内存,4GB还是更多?(一)

    前言: cpu的位是指一次性可处理的数据量是多少,1字节=8位,32位处理器可以一次性处理4个字节的数据量,依次类推.32位操作系统针对的32位的CPU设计.64位操作系统针对的64位的CPU设计.操 ...

  2. SurvivalShooter学习笔记(四.敌人攻击)

    此案例中,敌人始终朝着玩家移动 到达攻击玩家范围时(身上的大的触发器被玩家触发时(敌人靠近玩家,当身上的触发器被触发,且对象是玩家时条件达成)) 隔一个时间端,打击玩家一下,对玩家造成伤害,玩家掉血, ...

  3. Android 扁平化button

    View 创建 colors.xml 文件定义两个颜色 1. <resources> 2.     <color name="blue_pressed">@ ...

  4. 第一次使用Xamarin就上手 - 安裝Xamarin

    http://xamarintech.blogspot.tw/2013/06/xamarin-xamarin-step-by-step-part1.html http://xamarintech.bl ...

  5. python 之 多线程

    一.多线程(具体可参照博文多进程---->http://www.cnblogs.com/work115/p/5621789.html) 1.函数式实现多线程 2.类实现多线程 3.多线程之线程锁 ...

  6. Gson 解析多层嵌套JSON数据

    http://stackoverflow.com/questions/14139437/java-type-generic-as-argument-for-gson

  7. 关于oracle的sequence和trigger。

    原先mysql中每个自增字段,在oracle中就需要建立一个sequence和一个trigger. 就算同一个表中有x个自增字段,那么就需要建立x个sequence和x和trigger. 实际中,我建 ...

  8. QSS类的用法及基本语法介绍

    QSS类的用法及基本语法介绍 目录 1. 何为Qt样式表2. 样式表语法基础3. 方箱模型4. 前景与背景5. 创建可缩放样式6. 控制大小7. 处理伪状态8. 使用子部件定义微观样式8.1. 相对定 ...

  9. 洛谷P4475 巧克力王国

    洛谷P4475 巧克力王国 题目描述 巧克力王国里的巧克力都是由牛奶和可可做成的. 但是并不是每一块巧克力都受王国人民的欢迎,因为大家都不喜欢过于甜的巧克力. 对于每一块巧克力,我们设 x 和 y 为 ...

  10. Python 实现获取【昨天】【今天】【明天】日期

    昨天 from datetime import date, timedelta yesterday = (date.today() + timedelta(days=-1)).strftime(&qu ...