0x02.犀牛书札记(ch4-6)

ch4 表达式(expression)和运算符(operator)

原始表达式(primary expression)
表达式最小单位,包含常量、直接量(直接在程序中出现的常数值)、关键字、变量。

数组直接量中的列表逗号之间的元素可以省略,省略的空位会填充undefined。列表结尾处可以留下单个逗号,不会创建一个新的值undefined元素。

函数定义表达式可称为函数直接量。

1
var square = function(x){ return x*x; }

属性访问表达式 会首先计算表达式,如果结果是null或者undefined,就抛出一个类型错误异常。expression结果不是对象或者数组,会将其转换为对象。

1
2
3
expression.identifier
//属性名称是一个保留字或者包含空格和标点符号,或是一个数字(对于数组来说);或属性名是通过运算得出的值而不是固定的值的时候
expression[expression]

调用表达式(invocation expression)
调用(或者执行)函数或方法的语法。

如果表达式是一个属性访问表达式,那么调用称做“方法调用”(method invocation)。
在方法调用中,执行函数体的时候,作为属性访问主题的对象和数组便是其调用方法内this的指向。这种特性使得在面向对象编程范例中,函数(其OO名称为“方法”)可以调用其宿主对象。

对象创建表达式(object creation expression)
创建一个对象并调用一个函数(这个函数称做构造函数)初始化新对象的属性

1
2
//如果不需要传入任何参数给构造函数的话,圆括号是可以省略掉的
new Date

4.7 运算符

左值 lvalue(变量、数组元素或对象属性) 表达式只能出现在赋值运算符的左侧。 自定义的函数不能返回左值。

属性访问表达式和调用表达式的优先级最高。

4.8 算术表达式

所有那些无法转换为数字的操作数都转换为 NaN 值。 如果操作数(或者转换 结果)是NaN值, 算术运算的结果也是NaN。

1
2
3
4
5
6
7
8
9
10
11
5/2 //=>2.5 所有数字都是浮点类型
n/0 // n不等于0 => Infinity or -Infinity
0/0 // NaN
// % 结果跟被除数符号保持一致
5%2 //=>1
5%-2 //=>1
-5%2 //=>-1
//浮点数求余
6.5%2.1 //=>0.19999999999999973

4.8.1 +运算符

如果两个操作数都不是类字符串(string-like)的,将进行算符加减法。

  • 如果其中一个操作数是对象,则对象会遵循对象到原始值的转换规则转换为原始类值:日期对象通过toString()方法执行转换,其他对象则通过valueOf()方法执行转换(如果valueOf()方法返回一个原始值的话)。由于多数对象都不具备可用的valueOf()方法,因此它们会通过toString()方法来执行转换。
  • 在进行了对象到原始值的转换后,如果其中一个操作数是字符串的话,另一个操作数也会转换为字符串,然后进行字符串连接。
  • 否则,两个操作数都将转换为数字(或者NaN),然后进行加法操作。
1
2
3
true+true // => 2 布尔数值转换成数字
2+null // => 2 :null 转换为0后做加法
2+undefined //=>NaN : undefined转换为NaN做加法

4.8.3 位运算符

位运算符将NaN Infinity -Infinity 转换为0

4.9 关系表达式

=== 严格相等运算符(strict equality) 恒等运算符(identity operator)
== 相等运算符(equality operator) 相等允许进行类型转换

===

  • 两值 为null 或undefined,不相等。
  • 如果其中一个NaN或者都是NaN,则不相等。 x !== x 为true x为NaN
  • 0===-0

==
两个操作数类型相等,和严格相等比较规则一样。
两个操作数类型不同,会进行类型转换

  • null == undefined
  • 一个数字 一个字符串,先讲字符串转换为数字,然后比较
  • true 转化为 1 false转换为 0
1
2
//其他不同类型之间的比较均不相等
"1"==true

4.9.2 比较运算符

  • 如果操作数为对象,那么这个对象将转换为原始值:如果valueOf()返回一个原始值,那么直接使用这个原始值。否则,使用toString()的转换结果进行比较操作。
  • 在对象转换为原始值之后,如果两个操作数都是字符串,那么将依照字母表的顺序对两个字符串进行比较,这里提到的“字母表顺序”是指组成这个字符串的16位Unicode字符的索引顺序。
  • 在对象转换为原始值之后,如果至少有一个操作数不是字符串,那么两个操作数都将转换为数字进行数值比较。0和-0是相等的。Infinity比其他任何数字都大(除了Infinity本身),-Infinity比其他任何数字都小(除了它自身)。如果其中一个操作数是(或转换后是)NaN,那么比较操作符总是返回false.

4.9.3 in运算符

1
2
3
4
5
//如果右侧的对象拥有一个名为左操作数值的属性名,返回true
var data=[7,8,9];
"0" in data // => true 数组包含元素"0
1 in data // => true 数字转换为字符串
3 in data // => false 没有索引为3的元素

原型链 prototype chain 作为JavaScript的继承机制。

4.9.4 instanceof运算符

判断是一个对象是否为类的实例。
原型链 prototype chain 作为JavaScript的继承机制。

4.10 逻辑表达式

求X的等价布尔值 => !!X

4.11 赋值表达式

1
2
3
4
5
6
// a op= b 等价于 a=a op b
//后者 a计算了两次,如果a包含具有副作用的表达式(函数调用、赋值)时,两者不等价。
var data = [7,8,9]; var i=0;
//存在副作用 不等价
data[i++] *= 2;
data[i++] = data[i++]*2; //先计算左值 data[0]=data[1]*2

4.12 eval()

只有一个参数。

  • 如果传入的参数不是字符串,它直接返回这个参数。
  • 如果参数是字符串,它会把字符串当成JavaScript代码进行编译(parse),如果编译失败则抛出一个语法错误(SyntaxError)异常。
  • 编译成功,开始执行这段代码,并返回字符串的最后一个表达式或语句的值,如果最后一个表达式或语句没有值,则最终返回undefined.
  • 如果字符串抛出一个异常,这个异常将把该调用传递给eval()。

eval()使用了调用它的变量作用域环境。它查找变量的值和定义新变量和函数的操作和局部作用域中的代码完全一样。

1
2
3
console.log(temp) // temp is not defined
eval("temp=1;") // 1
console.log(temp); // 1

eval的字符串执行时的上下文环境和调用函数的上下文环境是一样的

1
2
3
4
var foo=function(a){eval(a);};
// 异常 Illegal return statement
// return 语句只能放在function中
foo("return;");

直接使用非限定的”eval”名称来调用eval()函数,称为“直接eval”(direct eval) 。ES5直接调用eval()时,它总是在调用它的上下文作用域内执行。其他的间接调用则使用全局对象作为其上下文作用域,并且无法读、写、定义局部变量和函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var geval = eval; // 使用别名调用eval将是全局eval
var x = "global", y = "global"; // 两个全局变量
function f() { // 函数内执行的是局部eval
var x = "local"; // 定义局部变量
eval("x += 'changed';"); // 直接eval更改了局部变量的值
return x; // 返回更改后的局部变量
}
function g() { // 这个函数内执行了全局eval
var y = "local"; // 定义局部变量
geval("y += 'changed';"); // 间接调用改变了全局变量的值
return y; // 返回未更改的局部变量
}
console.log(f(), x); // 更改了局部变量:输出"localchanged global":
console.log(g(), y); // 更改了全局变量:输出"local globalchanged":

typeof运算符

typeof x == typeof(x) 主要区分原始值和对象值

x typeof x
undefined “undefined”
null “object”
true或false “boolean”
任意数字或NaN “number”
任意字符串 “string”
任意函数 “function”
任意内置对象(非函数) “object”
任意宿主对象 由编译器各自实现的字符串,但不是”undefined”、”boolean”、”num-ber”或”string”

函数 & “可执行的对象”(callable object)

  • 所有的函数都是可执行的(callable),但是对象也有可能是可执行的,可以像调用函数一样调用它,但它并不是一个真正的函数。
  • ECMAScript 5规范则扩充至所有可执行对象,包括内置对象(native object)和宿主对象(hosTobject),所有可执行对象进行typeof运算都将返回“function”。
  • 大多数浏览器厂商也将JavaScript的原生函数对象(native function object)当成它们的宿主对象的方法来使用。

delete运算符

1
2
3
4
var a = [1, 2, 3]; // 定义一个数组
delete a[2]; // 删除最后一个数组元素
2 in a; // => false:元素2在数组中已经不存在了
a.length // => 3:注意,数组长度并没有改变,尽管上一行代码删除了这个元素,但删除操作留下了一个“洞”,实际上并没有修改数组的长度,因此 a数组的长度仍然是 3
  • 操作数不是左值,那么delete将不进行任何操作同时返回true
  • 通过var语句声明的变量不能删除。
  • 通过function语句定义的函数和函数参数也不能删除。

ch5 语句(statement)

函数定义不能出现在if语句、while循环或其他任何语句中。

函数定义表达式
函数定义语句中的函数被显示地提前到脚本或函数的顶部
函数声明语句
函数名称和函数体均提前:脚本中的所有函数和函数中所有嵌套的函数都会在当前上下文中其他代码之前声明。可以在声明一个JavaScript函数之前调用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//函数定义表达式
function x1()
{
//x2(); //function is not defined
console.log(typeof x2); //undefined
console.log(x2 instanceof Object); //false
var x2=function(){
console.log(2);
};
}
x1();
// 函数声明语
function x(){
var t=2;
t=y(t);
function y(param)
{
return param*2;
}
return t;
}
function x(){
var t=2;
t=y(t);
return t;
function y(param)
{
return param*2;
}
}

switch 按照“===”运算符比较case子句中的表达式是否和expression的值相同。

for/in
只有“可枚举”(enumerable)的属性才会遍历到。

1
2
3
4
5
6
7
8
9
10
11
//JavaScript解释器首先计算object表达式。
//如果表达式为null或者undefined,JavaScirpt解释器将会跳过循环并执行后续的代码。
//如果表达式等于一个原始值,这个原始值将会转换为与之对应的包装对象(wrapper object)
for (variable in object)
statement
//中variable的值可以当做赋值表达式的左值
var o = {x:1, y:2, z:3};
var a = [],
i = 0;
for (a[i++] in o) /* empty */; //a => ["x", "y", "z"]

如果没有return语句,则函数调用调用表达式的结果是undefined。return语句单独使用,函数调用程序返回undefined。

catch 从句参数具有块级作用域效果。

with语句
with语句用于临时扩展作用域链(在严格模式中是禁止使用with语句的,并且在非严格模式里也是不推荐使用with语句的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//将object添加到作用域链的头部,然后执行statement,最后把作用域链恢复到原始状态
with(object)
statement
//对象嵌套层次很深的时候通常会使用with语句来简化代码编写
//document.forms[0].address.value
with(document.forms[0]) {
// 直接访问表单元素,例如:
name.value = "";
address.value = "";
email.value = "";
}
//等价代码
var f = document.forms[0];
f.name.value = "";
f.address.value = "";
f.email.value = "";

with语句提供了一种读取对象的属性的快捷方式,但它并不能创建属性。

1
2
3
//如果对象o有一个属性x,那么这行代码给这个属性赋值为1。
//如果对象o没有定义属性x,它给一个局部变量或者全局变量x赋值,或者创建全局对象的一个新属性。
with(o) x = 1;

debugger语句
这条语句用来产生一个断点(breakpoint)

“use strict”
ES5引入的指令 说明(脚本或函数中)后续的代码将会解析为严格代码(strict code)。

  • 指令和普通的语句之间的区别 指令不包含任何语言的关键字,仅仅是一个包含一个特殊字符串直接量的表达式(可以是使用单引号也可以使用双引号)。
  • 指令只能出现在脚本代码的开始或者函数体的开始、任何实体语句之前。但它不必一定出现在脚本的首行或函数体内的首行。

严格模式

  • 禁止使用with语句
  • 在严格模式中,所有的变量都要先声明,如果给一个未声明的变量、函数、函数参数、catch从句参数或全局对象的属性赋值,将会抛出一个引用错误异常(在非严格模式中,这种隐式声明的全局变量的方法是给全局对象新添加一个新属性)。
  • 在严格模式中,调用的函数(不是方法)中的一个this值是undefined。(在非严格模式中,调用的函数中的this值总是全局对象)。
1
var hasStrictMode = (function() { "use strict"; return this===undefined}()); //判断JavaScript实现是否支持严格模式
  • 在严格模式中,当通过call()或apply()来调用函数时,其中的this值就是通过call()或apply()传入的第一个参数(在非严格模式中,null和undefined值被全局对象和转换为对象的非对象值所代替)。
  • 在严格模式中,给只读属性赋值和给不可扩展的对象创建新成员都将抛出一个类型错误异常(在非严格模式中,这些操作只是简单地操作失败,不会报错)。
  • 在严格模式中,传入eval()的代码不能在调用程序所在的上下文中声明变量或定义函数,而在非严格模式中是可以这样做的。相反,变量和函数的定义是在eval()创建的新作用域中,这个作用域在eval()返回时就弃用了。
  • 严格模式中,函数里的arguments对象拥有传入函数值的静态副本。在非严格模式中,arguments对象具有“魔术般”的行为,arguments里的数组元素和函数参数都是指向同一个值的引用
  • 在严格模式中,当delete运算符后跟随非法的标识符(比如变量、函数、函数参数)时,将会抛出一个语法错误异常(在非严格模式中,这种delete表达式什么也没做,并返回false)。
  • 在严格模式中,试图删除一个不可配置的属性将抛出一个类型错误异常(在非严格模式中,delete表达式操作失败,并返回false)。
  • 在严格模式中,在一个对象直接量中定义两个或多个同名属性将产生一个语法错误(在非严格模式中不会报错)。
  • 在严格模式中,函数声明中存在两个或多个同名的参数将产生一个语法错误(在非严格模式中不会报错)。
  • 在严格模式中是不允许使用八进制整数直接量(以0为前缀,而不是0x为前缀)的(在非严格模式中某些实现是允许八进制整数直接量的)。
  • 在严格模式中,标识符eval和arguments当做关键字,它们的值是不能更改的。不能给这些标识符赋值,也不能把它们声明为变量、用做函数名、用做函数参数或用做catch块的标识符。
  • 在严格模式中限制了对调用栈的检测能力,在严格模式的函数中,arguments.caller和arguments.callee都会抛出一个类型错误异常。严格模式的函数同样具有caller和argu-ments属性,当访问这两个属性时将抛出类型错误异常。

ch6 对象

“原型式继承”(prototypal inheritance)是JavaScript的核心特征。
对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)和枚举(enumerate)它的属性。

属性名可以包含空字符串在内的任意字符串。

“属性特性”(property attribute)

  • 可写(writable attribute),表明是否可以设置该属性的值。
  • 可枚举(enumerable attribute),表明是否可以通过for/in循环返回该属性。
  • 可配置(configurable attribute),表明是否可以删除或修改该属性。

对象特性(object attribute)

  • 对象的原型(prototype)指向另外一个对象,本对象的属性继承自它的原型对象。
  • 对象的类(class)是一个标识对象类型的字符串。
  • 对象的扩展标记(extensible flag)指明了(在ES5中)是否可以向该对象添加新属性。

相关术语

  • 内置对象(native object)是由ECMAScript规范定义的对象或类。例如,数组、函数、日期和正则表达式都是内置对象。
  • 宿主对象(hosTobject)是由JavaScript解释器所嵌入的宿主环境(比如Web浏览器)定义的。客户端JavaScript中表示网页结构的HTMLElement对象均是宿主对象。既然宿主环境定义的方法可以当成普通的JavaScript函数对象,那么宿主对象也可以当成内置对象。
  • 自定义对象(user-defined object)是由运行中的JavaScript代码创建的对象。
  • 自有属性(own property)是直接在对象中定义的属性。
  • 继承属性(inherited property)是在对象的原型对象中定义的属性。

6.1创建对象(create)

以通过对象直接量、关键字new和(ES5中的)Object.create()函数来创建对象

对象直接量
对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。如果值存在计算,那么多次调用中属性值可能不同。
最后一个属性后逗号将忽略。

关键字new
new 后跟跟随一个构造函数(constructor),初始化一个新创建的对象。

原型链”(prototype chain)

  • 所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过Object.prototype获得对原型对象的引用。
  • 通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。也继承自Object.prototype。通过new Array()创建的对象的原型就是Array.prototype,通过new Date()创建的对象的原型就是Date.prototype。
  • Object.prototype没有原型的对象。它不继承任何属性。
    其他原型对象都是普通对象,普通对象都具有原型。
  • 所有的内置构造函数(以及大部分自定义的构造函数)都具有一个继承自Object.prototype的原型。例如,Date.prototype的属性继承自Object.prototype,因此由new Date()创建的Date对象的属性同时继承自Date.prototype和Object.prototype。
  • null没原型

Object.create()
Object.create()是一个静态函数,而不是提供给某个对象调用的方法。

1
2
3
4
5
6
7
var o1 = Object.create({x:1, y:2}); // o1继承了属性x和y
//创建一个没有原型的新对象
var o2 = Object.create(null); //o2不继承任何属性和方法
//创建一个普通的空对象
var o3 = Object.create(Object.prototype); //o3和{}和new Object()一样

inherit()防止库函数无意间修不受控制的对象。修改继承对象的属性值,只会影响继承对象自身。

1
2
3
4
5
6
7
8
9
10
11
12
13
// inherit() 返回了一个继承自原型对象p的属性的新对象
// 这里使用ECMAScript 5中的Object.create()函数(如果存在的话)
// 如果不存在Object.create(),则退化使用其他方法
function inherit(p) {
if (p == null) throw TypeError(); // p是一个对象,但不能是null
if (Object.create) // 如果Object.create()存在
return Object.create(p); // 直接使用它
var t = typeof p; // 否则进行进一步检测
if (t !== "object" && t !== "function") throw TypeError();
function f() {}; // 定义一个空构造函数
f.prototype = p; //将其原型属性设置为p
return new f(); //使用f()创建p的继承对象
}

6.2 属性的查询和设置

数组元素是通过字符串索引而不是数字索引。这种数组就是我们所说的关联数组(associative array),也称做散列、映射或字典(dictionary)。JavaScript对象都是关联数组。

“自有属性”(own property) 只有在查询属性时才会体会到继承的存在,而设置属性则和继承无关 (override)

  • 属性赋值操作首先检查原型链,以此判定是否允许赋值操作。
  • 如果允许属性赋值操作,它也总是在原始对象上创建属性或对已有的属性赋值,而不会去修改原型链。

null和undefined值都没有属性,查询这些值的属性会报错、设置属性也会报类型错误。

1
var len = book &&book.subtitle &&book.subtitle.length;

对象设置属性失败场景

  • o中的属性p是只读的:不能给只读属性重新赋值(defineProperty()方法中有一个例外,可以对可配置的只读属性重新赋值)。
  • o中的属性p是继承属性,且它是只读的:不能通过同名自有属性覆盖只读的继承属性。
  • o中不存在自有属性p:o没有使用setter方法继承属性p,并且o的可扩展性(extensible attribute)是false。如果o中不存在p,而且没有setter方法可供调用,则p一定会添加至o中。但如果o不是可扩展的,那么在o中不能定义新属性。

6.3 删除

delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性
delete运算符只能删除自有属性,不能删除继承属性。

1
2
a={p:{x:1}};b=a.p;delete a.p;
b.x // => 1 已删除的引用依然存在

当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,它返回true。如果delete后不是一个属性访问表达式,delete同样返回true:

1
2
3
4
5
o = {x:1}; // o有一个属性x,并继承属性toString
delete o.x; // 删除x,返回true
delete o.x; // 什么都没做(x已经不存在了),返回true
delete o.toString; // 什么也没做(toString是继承来的),返回true
delete 1; // 无意义,返回true

delete不能删除那些可配置性为false的属性,例如通过变量声明、函数声明创建的全局对象属性。
当在非严格模式中删除全局对象的可配值属性时,可以省略对全局对象的引用,直接在delete操作符后跟随要删除的属性名即可

1
2
this.x = 1; // 创建一个可配置的全局属性(没有用var)
delete x; // 将它删除

6.4 检测属性

判断某个属性是否存在于某个对象中

  • in运算符 如果对象的自有属性或继承属性中包含这个属性则返回true:
  • hasOwnPreperty() 是否是对象的自有属性。对于继承属性它将返回false:
  • propertyIsEnumerable() 只有检测到是自有属性且这个属性的可枚举性(enumerable attribute)为true时它才返回true。通常由JavaScript代码创建的属性都是可枚举的
  • 通过属性查询 使用“!==”判断一个属性是否是undefined

in可以区分不存在的属性和存在但值为undefined的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var o = { x: undefined } // 属性被显式赋值为undefined
o.x !== undefined // false:属性存在,但值为undefined
o.y !== undefined // false:属性不存在
"x" in o // true:属性存在
"y" in o // false:属性不存在
delete o.x; // 删除了属性x
"x" in o // false:属性不再存在
// 如果o中含有属性x,且x的值不是null或undefined,o.x乘以2.
if (o.x != null) o.x *= 2;
// 如果o中含有属性x,且x的值不能转换为false,o.x乘以2.
// 如果x是undefined、null、false、" " 、0或NaN,则它保持不变
if (o.x) o.x *= 2;

6.5 枚举属性

for/in循环可以在循环体中遍历对象中所有可枚举的属性(包括自有属性和继承的属性)。
对象继承的内置方法不可枚举的,但在代码中给对象添加的属性都是可枚举的。

1
2
3
4
5
6
for (p in o) {
if (!o.hasOwnProperty(p)) continue; // 跳过继承的属性
}
for (p in o) {
if (typeof o[p] === "function") continue; // 跳过方法
}

Object.keys(),它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成。
Object.getOwnPropertyNames(),返回对象的所有自有属性的名称,包括不可枚举.

6.6 getter setter

由getter和setter定义的属性称做“存取器属性”(accessor property),它不同于“数据属性”(data property),数据属性只有一个简单的值。
存取器属性不具有可写性(writable attribute).如果它只有setter方法,那么它是一个只写属性,读取只写属性总是返回undefined。存取器属性是可以继承的。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 返回随机数的存取器属性 例如,表达式"random.octet"产生一个随机数
// 每次产生的随机数都在0~255之间
var random = {
get octet() {
return Math.floor(Math.random() * 256);
},
get uint16() {
return Math.floor(Math.random() * 65536);
},
get int16() {
return Math.floor(Math.random() * 65536) - 32768;
}
};

6.7 属性的特性

  • 给原型对象添加方法,并将它们设置成不可枚举的,这让它们看起来更像内置方法。
  • 给对象定义不能修改或删除的属性,借此“锁定”这个对象。

数据属性的4个特性分别是它的值(value)、可写性(writable)、可枚举性(enumerable)和可配置性(configurable)。
存取器属性的4个特性是读取(get)、写入(set)、可枚举性和可配置性。

“属性描述符”(property descriptor)的对象

  • 数据属性的描述符对象的属性有value、writable、enumerable和configurable。
  • 存取器属性的描述符对象的属性有get、set、enumerable和configurable。
  • 其中writable、enumerable和configurable都是布尔值,get属性和set属性是函数值

Object.getOwnPropertyDescriptor() 获得自有属性的描述符。对于继承属性和不存在的属性,返回undefined。
要想获得继承属性的特性,需要遍历原型链 Object.getPrototypeOf()。

设置属性的特性 Object.defineProperty() Object.defineProperties()

1
2
3
4
5
//新创建的属性来说,默认的特性值是false或undefined
var o = {};
Object.defineProperty(o,'x',{value:1});
//{value: 1, writable: false, enumerable: false, configurable: false}
Object.getOwnPropertyDescriptor(o, 'x');
  • 如果对象是不可扩展的,则可以编辑已有的自有属性,但不能给它添加新属性。
  • 如果属性是不可配置的,则不能修改它的可配置性和可枚举性。
  • 如果存取器属性是不可配置的,则不能修改其getter和setter方法,也不能将它转换为数据属性。
  • 如果数据属性是不可配置的,则不能将它转换为存取器属性。
  • 如果数据属性是不可配置的,则不能将它的可写性从false修改为true,但可以从true修改为false。
  • 如果数据属性是不可配置且不可写的,则不能修改它的值。然而可配置但不可写属性的值是可以修改的(实际上是先将它标记为可写的,然后修改它的值,最后转换为不可写的)。

以两条下划线作前缀,两条下划线作后缀,以表明它们是非标准的方法

6.8 对象的属性

原型属性 prototype attribute
对象的原型属性是用来继承属性的。通常”o的原型属性” 叫 “o的原型”。
Object.getPrototypeOf() 查询对象的原型 (o.constructor.prototype方法不可靠)
isPrototypeOf() 检测一个对象是否是另一个对象的原型(或处于原型链中)

类属性 class attribute
表示对象的类型信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function classof(o) {
if (o === null) return "Null";
if (o === undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8, -1);
}
classof(null)//=>"Null"
classof(1)//=>"Number"
classof("")//=>"String"
classof(false)//=>"Boolean"
classof({})//=>"Object"
classof([])//=>"Array"
classof(/./) //=>"RegExp"
classof(new Date()) //=>"Date"
classof(window) //=>"Window"
function f(){}
classof(new f()) //=>"Object"

可扩展性属性 extensible attribute
表示是否可以给对象添加新属性。所有内置、自定义对象都是显式可扩展。 目的是将对象“锁定”,以避免外界的干扰。

  • Object.jsExtensible()来判断对象是否是可扩展的。
  • Object.preventExtensions()将对象转换为不可扩展的。一旦将对象转换为不可扩展的话就无法转换为可扩展的了。只影响对象本身的可扩展性,该对象的原型添加属性,该对象会继承新属性。
  • Object.seal() 能够将对象设置为不可扩展的,将对象的所有自有属性都设置为不可配置的。
  • 不能给这个对象添加新属性,而且它已有的属性也不能删除或配置,不过它已有的可写属性依然可以设置。对于那些已经封闭(sealed)起来的对象是不能解封的。Object.isSealed()来检测对象是否封闭。
  • Object.freeze() “冻结”(frozen)。 将对象设置为不可扩展的和将其属性设置为不可配置的之外,还可以将它自有的所有数据属性设置为只读(如果对象的存取器属性具有setter方法,存取器属性将不受影响,仍可以通过给属性赋值调用它们)。 Object.isFrozen()来检测对象是否冻结。

6.9 序列化对象(serialization)

指将对象的状态转换为字符串,也可将字符串还原为对象.

  • NaN、Infinity和-Infinity序列化的结果是null
  • 日期对象序列化的结果是ISO格式的日期字符串(Date.toJSON()函数),但JSON.parse()依然保留它们的字符串形态,而不会将它们还原为原始日期对象。
  • 函数、RegExp、Error对象和undefined值不能序列化和还原。
  • JSON.stringify()只能序列化对象可枚举的自有属性。
坚持原创技术分享,您的支持将鼓励我继续创作!