Skip to content
LYF edited this page Sep 23, 2016 · 25 revisions

一、作为函数调用 Function 构造器

当将 Function 作为函数来调用,而不是作为构造器,它会创建并初始化一个新函数对象。所以函数调用 Function(…) 与用相同参数的 new Function(…) 表达式创建的对象相同。

Function (p1, p2, … , pn, body)

当以 p1, p2, … , pn, body 作为参数调用 Function 函数(这里的 n 可以是 0,也就是说没有“p”参数,这时还可以不提供 body),采用如下步骤:创建并返回一个新函数对象,它仿佛是用相同参数给标准内置构造器 Function (15.3.2.1). 用一个 new 表达式创建的。

笔记:由于Function和new Function的结果是完全一样的,所以,可以省略掉new 来创建一个Function对象

二、in 操作符

产生式 RelationalExpression : RelationalExpression in ShiftExpression 按照下面的过程执行 :

 1. 令 lref 为解释执行 RelationalExpression 的结果 .
 2. 令 lval 为 GetValue(lref).
 3. 令 rref 为解释执行 ShiftExpression 的结果 .
 4. 令 rval 为 GetValue(rref).
 5. 如果 Type(rval) 不是 Object ,抛出一个 TypeError 异常 .
 6. 返回以参数 ToString(lval). 调用 rval 的 `[[HasProperty]]` 内置方法的结果

关于[[HasProperty]]:

这是一个所有对象(native object 和 host object)都要实现的属性

返回一个 Boolean 值,说明对象是否含有给定名称的属性。

笔记:可以看到,关于in操作符,并没有说可遍历属性的事儿,所以,in操作符是不管这个属性可不可遍历,不管这个属性是自有属性还是继承属性,都会被in操作符检索出来

测试:

function Person(name2){ this.name2 = name2};
function Student(id){ this.id = 123};
var p = new Person('李彦峰');
Object.defineProperty(p,'qq',{value:455322185});// 定义一个不可遍历属性age
Student.prototype = p // Student继承Person的一个实例
var s = new Student;
Object.defineProperty(s,'age',{value:23});// 定义一个不可遍历属性age
"name2" in s; // true
"age" in s; // true
"qq" in s; // true

三、ToPrimitive(obj)抽象操作

Object返回该对象的默认值。(调用该对象的内部方法[[DefaultValue]]一樣)。

[[DefaultValue]] SpecOp (Hint) → primitive Hint 是一个字符串。返回对象的默认值

所有对象(包括宿主对象)必须实现表 8 中列出的所有内部属性。然而,对某些对象的 [[DefaultValue]] 内部方法,可以简单的抛出 TypeError 异常。

当用字符串 hint 调用 O 的 [[DefaultValue]] 内部方法,采用以下步骤:

令 toString 为用参数 "toString" 调用对象 O 的 [[Get]] 内部方法的结果。

如果 IsCallable(toString) 是 true,则

令 str 为用 O 作为 this 值,空参数列表调用 toString 的 [[Call]] 内部方法的结果。

如果 str 是原始值,返回 str。

令 valueOf 为用参数 "valueOf" 调用对象 O 的 [[Get]] 内部方法的结果。

如果 IsCallable(valueOf) 是 true,则 令 val 为用 O 作为 this 值,空参数列表调用 valueOf 的 [[Call]] 内部方法的结果。 如果 val 是原始值,返回 val。 抛出一个 TypeError 异常。

当用数字 hint 调用 O 的 [[DefaultValue]] 内部方法,采用以下步骤:

令 valueOf 为用参数 "valueOf" 调用对象 O 的 [[Get]] 内部方法的结果。

如果 IsCallable(valueOf) 是 true,则

令 val 为用 O 作为 this 值,空参数列表调用 valueOf 的 [[Call]] 内部方法的结果。

如果 val 是原始值,返回 val。

令 toString 为用参数 "toString" 调用对象 O 的 [[Get]] 内部方法的结果。

如果 IsCallable(toString) 是 true,则

令 str 为用 O 作为 this 值,空参数列表调用 toString 的 [[Call]] 内部方法的结果。

如果 str 是原始值,返回 str。

抛出一个 TypeError 异常。

当不用 hint 调用 O 的 [[DefaultValue]] 内部方法,O 是 Date 对象的情况下仿佛 hint 是字符串一样解释它的行为,除此之外仿佛 hint 是数字一样解释它的行为。

上面说明的 [[DefaultValue]] 在原生对象中只能返回原始值。如果一个宿主对象实现了它自身的 [[DefaultValue]] 内部方法,那么必须确保其 [[DefaultValue]] 内部方法只能返回原始值。

笔记:先计算obj.valueOf(),如果结果是原始值,则返回此结果;否则计算obj.toString(),如果值是原始值,则返回此结果,否则抛出异常。 注:此处有个例外,即Date类型的对象,它会先调用toString()方法.

四、加号运算符 ( + )

The addition operator either performs string concatenation or numeric addition.

产生式 AdditiveExpression : AdditiveExpression + MultiplicativeExpression 按照下面的过程执行 :

令 lref 为解释执行 AdditiveExpression 的结果 .

令 lval 为 GetValue(lref).

令 rref 为解释执行 MultiplicativeExpression 的结果 .

令 rval 为 GetValue(rref).

令 lprim 为 ToPrimitive(lval).

令 rprim 为 ToPrimitive(rval).

如果 Type(lprim) 为 String 或者 Type(rprim) 为 String,则:

返回由 ToString(lprim) 和 ToString(rprim) 连接而成的字符串

返回将加法运算作用于 ToNumber(lprim) 和 ToNumber(rprim) 的结果。参见 11.6.3 后的注解。

在步骤5和6中的ToPrimitive调用没有提供hint,除了Date对象之外所有ECMAScript对象将缺少hint的情况当做Number处理;Date对象将缺少hint的情况当做hint为字符串。宿主对象可能将缺少hint的情况当做别的处理。

步骤7与关系运算符比较算法中的步骤3不同,它使用逻辑或运算符而不是逻辑与运算符

五、执行环境、词法环境、变量环境三者之间的关系

组件 作用目的 词法环境组件 指定一个词法环境对象,用于解析该执行环境内的代码创建的标识符引用。 变量环境组件 指定一个词法环境对象,其环境数据用于保存由该执行环境内的代码通过 VariableStatement 和 FunctionDeclaration 创建的绑定。 this 绑定 指定该执行环境内的 ECMA 脚本代码中 this 关键字所关联的值。

词法环境由词法环境和变量环境组成,可以认为词法环境和变量环境为执行环境上的一个属性。

其中执行环境的词法环境组件和变量环境组件始终为词法环境对象。当创建一个执行环境时,其词法环境组件和变量环境组件最初是同一个值。在该执行环境相关联的代码的执行过程中,变量环境组件永远不变,而词法环境组件有可能改变。

在本标准中,通常情况下,只有正在运行的执行环境(执行环境栈里的最顶层对象)会被算法直接修改。因此当遇到“词法环境组件”、“变量环境组件”、“this 绑定组件”这三个术语时,指的是正在运行的执行环境的对应组件。

执行环境是一个纯粹的标准机制,并不代表任何 ECMA 脚本实现的工件。在 ECMA 脚本程序中是不可能访问到执行环境的。

六、测试数据属性和访问器属性的一个容易让人忽略的不同点

规范描述:

[[prototype]]对象继承来的命名数据属性(作为子对象的属性可见)是为了[[get]]请求,但无法用户[[put]]请求 命名访问器属性会把[[get]][[put]]都继承

解读:

这都规范的意思是:如果是数据属性,那么给对象赋值的话,会赋值到对象本身,而不会赋值到对象的原型链上的同名属性上去

如果是访问器属性,那么给对象赋值的话,会调用原型链上的set方法

代码验证:

var object = {};
    var obj = Object.create(object);
    Object.defineProperties(object,{
        name: {
                value: '李彦峰',
                writable: true,
                enumerable: true,
                configurable: true
        },
        age: {
            get: function(){
                return this._age;
            },
            set: function(age){
                console.log("[[put]]触发");
                this._age = age;
            },
            enumerable: true,
            configurable: true
        }
    });
    console.log(obj);

    console.group('测试数据属性');
    console.log(Object.getOwnPropertyDescriptor(obj,'name'));
    console.log(Object.getOwnPropertyDescriptor(object,'name'));
    console.log(obj.name);
    obj.name = '李燕山';
    console.log(obj.name);
    console.log(obj);
    console.groupEnd('测试数据属性');

    console.group('测试访问器属性');
    console.log(Object.getOwnPropertyDescriptor(obj,'age'));
    console.log(Object.getOwnPropertyDescriptor(object,'age'));
    console.log(obj.age);
    // 原来obj对象并没有age属性,给obj的age属性赋值的话
    // 会调用obj对象的原型对象即object的age访问器属性的set方法
    obj.age = 26;
    console.log(obj.age);
    console.log(obj);
    console.groupEnd('测试访问器属性');
Clone this wiki locally