一、问题的提出

  我们先来看看下面几段代码,要注意的是,以下代码不要在浏览器的开发者工具(如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. 2-XOR-SAT (种类并查集)

    写了那么多模拟题这题算是最难的了QAQ 好神,,,我于是补了一下并查集.. 并查集很神...... orz 种类并查集...orz 对于维护sat,我们可以这样想: 如果x和y的xor是true,那么 ...

  2. landa语法

    sg_orm看当前sql信息 db.IsEnableLogEvent = true; db.LogEventStarting = (sql, pa) => { var t = 0; }; //出 ...

  3. HDU 5326(2015多校3)-Work(dfs)

    题目地址:pid=5326">HDU 5326 题意:给一张有向图n个点.n - 1(....输入n-1)条边. A指向B代表A管理B.然后能够间接管理,比方A管理B,B管理C.则A管 ...

  4. 图表 Chart

    工作中,需要实现如下的图表,查阅了不少的资料,问了不少的人,下面对下图表的实现代码做下讲解. 实现代码: chart1.Series.Clear();//清空图表中的序列,图表中有默认的序列 //ch ...

  5. java -Mac搭建本地服务器并映射到外网

    最近在学习Html,小有进步变想着写一个浪漫的静态页面给女朋友浪漫一下,那么问题就来了,如何把我的网页让对网络一窍不通的女朋友看到,所以便想到了是用自己电脑作为服务器的想法.百度以后整理如下: 首先搭 ...

  6. ng-template

    一,锚点链接 定义锚点: <a href="#login" class="btn btn-primary">Login</a> < ...

  7. angular_文本变化

    注意,在input中用ng-change的时候,一定要结合着ng-model用 开头,注意在这里添加了ng-app <!DOCTYPE html> <html lang=" ...

  8. 7.15;linux命令

    麦克维瀑布 https://farm5.staticflickr.com/4269/34749113172_d6c1ba274a_k.jpg ----------------------------- ...

  9. 【BZOJ4567】[Scoi2016]背单词 Trie树+贪心

    [BZOJ4567][Scoi2016]背单词 Description Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”.这时候睿智 的凤老师从远处飘来,他 ...

  10. Android 生成keystore,两种方式

    一.eclipse 中生成android keystore 建立任意一个android项目(例如:AntForAndroid) 右键AntForAndroid根目录弹出菜单->Android T ...