语句
JavaScript中没有块级作用域,所以在语句块中声明的变量并不是语句块私有的
JavaScript需要循环体中至少包含一条语句,因此,当没有要写的语句时,可以使用一个单独的分号来表示一条空语句
//初始化一个数组a
for(i = 0; i < a.length; a[i++] = 0);
var声明的变量是无法通过delete删除的,未指定是,初始值为 undefined;变量声明语句会被提前至脚本或函数的顶部,但初始化的操作还是在原来var语句的位置执行,到var语句之前,该变量的值是undefined;在循环之中 var语句也同前
try、catch和finally语句块需要使用花括号括起来,这里的花括号是不可以省略的
语句小结:
- break:退出最内存循环或退出switch,或退出label指定的语句
- continue:重新开始最内存循环或label指定的循环
- debugger:端点器调节
- return:从函数返回一个值
- use strict:对脚本和函数应用严格模式
- with:扩展作用域链(不赞成使用)
对象
除了字符串、数字、true、false、null和undefined,js中的值都为对象
对象时可变的,我们通过引用而非值来操作对象,如果变量x是指向一个对象的引用,那么执行 var y = x; 变量y也是指向同一个对象的引用,而非这个对象的副本。通过变量y的修改,这个独享亦会对变量x造成影响
对象的属性名:可以是包含空字符串在内的任意字符串;但不能重名。对象的属性值可以是任意js值,或 .getter\.setter 函数
对象的创建
- 使用对象直接量
var empty = {}; var book = { "main title":"JavaScript", // 属性名中有空格的,必须用字符串表示 "sub-title":"The Definitive", // 属性名中有连字符的,必须用字符串表示 "for":"all audiences", // “for” 为保留字,必须用引号 author:{ // 对象也可作为一属性存在 x:point.x, // 下面的这些属性名就没有必要用引号 y:point.y+1 } };
- 直接通过 new 创建
var o = new Object(); // 空对象 var a = new Array(); // 空数组 var d = new Date(); // 表示当前时间的Date对象 var r = new RegExp("js"); // 可以进行模式匹配的RegExp对象
- 原型 —— 每个JavaScript对象(null除外)都和另一个对象相关联,即为原型,每个对象都是从原型继承属性;上两种创建的对象也继承自 Object.prototype
Object.prototype // 获得对原型对象的引用
-
Object.create() —— 用它创建一个新对象,第一个参数为这个对象的原型,第二个参数可选,用以对对象的属性进行进一步描述;这是一个静态函数,使用时直接传入需要的原型对象即可
var o1 = Object.create({x:1,y:2}); // o1继承了原型中的x,y属性 var o2 = Object.create(null); // o2没有原型,不继承任何属性和方法 var o3 = Object.create(Object.prototype); // 创建一个普通的空对象,和{},new Object()一样 // 通过原型继承创建一个新对象 // 方法返回一个继承自原型对象p的属性的新对象(这个方法的实现并没有特别完善) // 用途一:防止库函数无意间修改那些不受控制的对象,对继承对象的操作只会影响继承对象自身,而不是原始对象 function inherit(p){ if(p == null){ throw TypeError(); } // 如果 Object.create() 存在,直接使用创建就好 if(Object.create){ return Object.create(p); } var t = typeof p; if(t !== "object" && t !== "function"){ throw TypeError(); } // 定义一个空构造函数 function f(){}; // 将其原型属性设置为 p f.prototype = p; return new f(); }
属性的查询与设置
点运算符后面的标识符不能是保留字,如o.for等;
如果一个对象的属性名是保留字,则必须用方括号的形式访问,但在 ECMAScript 5 中,可以在点运算符后直接使用保留字;
当不确定属性名时,无法通过(.)来访问对象的属性(因为标识符是静态的,必须写死在程序中),但可以使用 [ ] (因为字符串是动态的,可以在运行时更改)
/*
获取属性的值:
- 运算符左侧为一个表达式,返回一个对象
- (.):右侧必须是一个以属性名称命名的简单标识符
- ([]):方括号内必须是一个计算结果为字符串的表达式,这和字符串就是属性的名字
*/
var author = book.author; // 得到book的 “author” 属性
var title = book["main title"]; // 得到book的“main title”属性
/*
创建属性或给属性赋值,用法同上
*/
book.eadition = 6; // 给 book 创建一个名为“editor” 属性并赋值
book["main title"] = "ECMAScript"; // 给“mian title” 属性赋值
查询一个不存在的属性并不会报错,返回 undefined;但如果对象不存在,视图查询这个不存在的对象的属性就会报错,下面提供两种避免错误的方法:
//原句
var len = book.subtitle.length; // 抛出一个类型错误异常,undefined没有length属性
//第一种:冗余但易懂
var len = undefined;
if(book) {
if(book.subtitle) {
len = book.subtitle.length;
}
}
//第二种:简练、常用
var len = book && book.subtitle && book.subtitle.length;
删除属性
delete只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除,且会影响所有继承自这个原型的对象)
delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性
a = { p:{x:1}};
b = a.p;
delete a.p; // 执行结束后,b.x还是等于1
// 以上这种不严谨的代码可能会导致内存泄漏,所以在销毁对象的时候,要遍历属性中的属性,一次删除
当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,它返回 true;如果 delete后不是一个属性访问表达式,delete同样返回true
o = {x:1};
delete o.x; //true
delete o.x; //true 什么也没做(x已不存在了)
delete o.toString(); //true 什么也没做(toString是继承来的)
delete 1; //true 无意义
delete不能删除可配置性为false的属性(可删除不可扩展对象的可配置属性),比如通过变量声明和函数声明创建的全局对象的属性
delete Object.prototype; // 不能删除,属性是不可配置的
var x = 1; // 声明一个全局变量
delete this.x; // 不能删除这个属性
function f() {} // 声明一个全局函数
delete this.f; // 不能删除全局函数
this.x = 1; // 创建一个可配置的全局属性(没有用 var)
delete x; // 在严格模式下会报语法错误
delete this.x; // 正常工作
检测属性
- in运算符:左侧属性名,右侧对象,若对象的自由属性或继承属性中包含这个属性则返回 true
- hasOwnProperty():检测给定的名字是否是对象的自有属性,对于继承属性将返回 false
- propertyIsEnumerable():hasOwnProperty()增强版,只有检测到为自有属性且这个属性的可枚举性为true时才返回true
- " !== ":判断一个属性是否是undefined;但当属性存在且值为undefined时,!== 区分不出来
// in运算符
var o = { x: 1 }
"x" in o; // true
"y" in o; // false
"toString" in o; // true
// hasOwnProperty()
var o = { x: 1 }
o.hasOwnProperty("x"); // true
o.hasOwnProperty("y"); // false
o.hasOwnProperty("toString"); // false
// propertyIsEnumerable()
var o = inherit({ y: 2 });
o.x = 1;
o.propertyIsEnumerable("x"); // true
o.propertyIsEnumerable("y"); // false
Object.prototype.propertyIsEnumerable("toString"); //false
// !==
var o = { x: 1 }
o.x !== undefined; // true
o.y !== undefined; // false
o.toString !== undefined; //true
属性的特性
属性除了名字和值(value)以外,还包含一些标识可写(writable)、可枚举(enumerable)和可配置(configurable)的特性;存取器属性不具有值和可写性,改为读取(get)、写入(set)
Object.getOwnPropertyDescriptor():可以获得某个对象特定属性的属性描述符(只限于自有属性)
Object.getPropertyOf():要想获得继承属性的特性,需要遍历原型链
// 返回 {value:1, writable:true, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor({ x: 1 },"x");
var random = {
// 产生一个 0~255 之间的随机数
get octet() { return Math.floor(Math.random()*256); }
};
// 查询 random 对象中的 octet 属性
// 返回 { get:/*func*/,set:undefined,enumerable:true,configurable:true }
Object.getOwnPropertyDescriptor(random, "octet");
// 对于继承属性和不存在的属性,返回 undefined
Object.getOwnPropertyDescriptor({}, "x"); // undefined,没有这个属性
Object.getOwnPropertyDescriptor({}, "toString"); // undefined,继承属性
Object.definePeoperty():设置属性的特性(同样的,不能修改继承属性)
Object.defineProperties():同时修改或创建多个属性;第一个参数:修改的对象,第二个参数:映射表,包含要新建或修改的属性的名称,以及它们的属性描述符
上两方法使用规则,违反抛类型错误异常:
- 如果对象时不可扩展的,只可编辑已有属性,不能添加属性
- 如果属性是不可配置的,则不能修改可配置性和可枚举性
- 如果存取器属性是不可配置的,则不能修改其getter和setter方法,也不能将其转为数据属性
- 如果数据属性是不可配置的,则不能将其转为存取器属性
- 如果数据属性是不可配置的,则不能将它的可写性从false修改为true,但可以从true修改为false
- 如果数据属性是不可配置的且不可写,则不能修改它的值。然而可配置但不可写的属性的值是可以修改的(实际上时先将它标记为可写的,然后修改它的值,最后转换为不可写的)
var o = {};
// 添加一个不可枚举的数据属性 x,并赋值为1
Object.defineProperty(o, "x", { value: 1,
writable: true,
enumerable: false,
configurable: true });
// 属性存在,但不可枚举
Object.ket(o) // => []
// 对属性 x 做修改,让其变为只读
Object.defineProperty(o, "x", { writable: false });
// 更改属性的值,操作失败不报错,严格模式下抛出类型错误异常
o.x = 2;
// 将 x 从数据属性修改为存取器属性
Object.defineProperty(o, "x", { get: function() {return 0;} });
o.x // => 0
// 同时修改或创建多个属性
var p = Object.defineProperties({}, {
x: {value: 1, writable: true, enumerable: true, configurable: true},
y: {value: 1, writable: true, enumerable: true, configurable: true},
r: {
get: function() { return Math.sqrt(this.x*this.x + this.y*this.y) },
enumerable: true,
configurable: true
}
});
原型属性
p.isPrototypeOf(o):检测 p 是否是 o 的原型
_proto_ 属性:直接查询、设置对象的原型,但不推荐使用。部分浏览器未实现,且将来可能也不会实现。实现了 ECMAScript 5的Firefox版本依然支持,只是对修改不可扩展对象的原型做了限制
var p = { x: 1 };
var o = Object.create(p);
p.isPrototypeOf(o) // true
Object.prototype.isPrototypOf(o) // true; p继承自 Object.prototype
类属性
可以通过调用对象的 toString() 方法,然后提取已返回字符串的第8个到倒数第二个位置之间的字符,来获得对象的类。但很多对象继承的 toString() 方法重写了,为了能调用正确的 toString() 版本,必须间接地调用 Function.call() 方法
// 返回传递进来任意对象的类
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"
可扩展性
对象的可扩展性用以表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显式可扩展的,宿主对象的可扩展性是由JavaScript引擎定义的
Object.esExtensible():将对象传入,判断该对象是否是可扩展的
Object.preventExtensions():<返回传入的对象> 将待转换的对象作为参数传进去,可以将对象转为不可扩展的。注意:一旦将对象转为不可扩展的,就无法再将其转换回可扩展的了;同样,这个方法只会影响到对象本身的可扩展性,当一个不可扩展的对象的原型添加了新属性,这个不可扩展的对象同样会继承这些新属性
Object.seal():<返回传入的对象> 除了能将对象设置为不可扩展的,还能将对象的所有自有属性都设置为不可配置的。即不能给这个对象添加新属性,已有的属性也不能删除或配置,但已有的可写属性依然可以设置,对弈已经封闭的对象是不能解封的
Object.isSealed():检测对象是否封闭
Object.freeze():<返回传入的对象> 更严格地锁定对象。除了将对象设置为不可扩展的和将其属性设置为不可配置的之外,还可以将其自有的所有数据属性设置为只读(如果对象的存取器属性具有setter方法,存取器属性将不受影响,仍可以通过给属性赋值调用它们)
Object.isFrozen():检测对象是否冻结
// 创建一个封闭的对象,包括一个冻结的原型和一个不可枚举的属性
var o = Object.seal(Object.create(Object.freeze({x: 1}),
{y: {value: 2,writable: true}}));
序列化对象
对象序列化是指将对象的状态转换为字符串,也可将字符串还原为对象
JSON.stringify():序列化,只能序列化对象可枚举的自有属性
JSON.parse():还原JavaScript对象
使用JSON作为数据交换格式。JSON支持对象、数组、字符串、无穷大数字、true、false、null,可序列化和还原。NaN、Infinity和-Infinity序列化结果:null;日期对象序列化的结果是ISO格式的日期字符串
函数、RegExp、Error对象和undefined值不能序列化和还原
对于一个不能序列化的属性来说,在序列化后的输出字符串中会将这个属性省略掉