JavaScript权威指南(第六版) 读书杂记二

语句

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值不能序列化和还原

对于一个不能序列化的属性来说,在序列化后的输出字符串中会将这个属性省略掉

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值