二、提供全局的JSON对象
序列化和反序列化概念:
(1)序列化:将内存中的对象转化为字节序列,用于持久化到磁盘中或者通过网络传输。对象序列化的最主要的用处就是传递和保存对象,保证对象的完整性和可传递性。
序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。序列化后的字节流保存了对象的状态以及相关的描述信息。序列化机制的核心作用就是
对象状态的保存与重建。
(2)反序列化:从字节序列创建对象的过程称为反序列化。序列化对象和平台无关,序列化得到的字节流可以在任何平台反序列化。从文件中或网络上获得序列化的字节流后,
根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。
2.1 JSON.stringify( ) 对象的序列化,js对象(数组)转换为json对象(数组)
JSON.stringify(obj/arr,callbackfn)
参数:obj 必需;要处理的对象
callbackfn 可选;回调函数,函数中有两个参数如下:
第一个参数:属性名
第二个参数:属性值
// 需求一:对象转JSON串的过程中,将 数字字符串 作为 数字保留 //定义对象 var obj = { a:1, b:'2', c:{ d:3 } } //对象转JSON串 JSON.stringify() var result = JSON.stringify(obj,function(key,value){ // 对value进行判断 if(typeof value === "string"){ // 是字符串数字,通过 正(+)运算符转为纯数值 return +value;//这里 + 是 正负的正符号 数学运算 转为数值 } return value; }); console.log(result,"数据类型:",typeof result);//{"a":1,"b":2,"c":{"d":3}} 数据类型: string
2.2 JSON.parse( ) 对象的反序列化,json对象(数组)转换为js对象(数组)
JSON.parse(json,callbackfn)
参数:str 必需;要处理的json字符串
callbackfn 可选;回调函数,函数中有两个参数如下:
第一个参数:属性名
第二个参数:属性值
// 需求2:JSON串转对象过程中,将 数字字符串 转为 数字 // 创建JSON串 var jsonStr = '{"a":1,"b":"2","c":{"d":3}}'; // JSON串转对象 JSON.parse() var result2 = JSON.parse(jsonStr,function(key,value){ // 将 数字字符串 转为 数字 if(typeof value === "string"){ return +value; } return value; }); console.log(result2,"数据类型:",typeof result2);//a: 1, b: 2, c: {…}} "数据类型:" "object"
三、ES5新增对象特性
3.1 对象属性特性
Object.defineProperty((obj,prop,descriptor) 方法可以精确地添加或修改对象上的属性。该方法用于设置单一属性特性
参数
obj 要在其上定义属性的对象
prop 要定义或修改的属性的名称
descriptor 定义或修改的属性的描述
返回值 传递给函数的对象
Object.defineProperties(obj, props) 方法直接在对象上定义新属性或修改现有属性,并返回该对象。
参数
obj 在其上定义或修改属性的对象。
props 一个对象,其键表示要定义或修改的属性的名称,其值是描述这些属性的对象。中的每个值props必须是数据描述符或访问器描述符
返回值 传递给函数的对象
注:Object.defineProperties本质上,定义props了与对象obj对象的可枚举自己的属性相对应的所有属性
// 在ES3.1中,只要对象能够访问到,就可以任意的去操作该对象,访问对象、添加属性、修改属性和删除属性,如下: console.group("ES3对象操作"); var stu = { name:"张三", sex:"男", age:23 } console.log("原对象stu:",stu); // 添加分数属性 stu.score = 100; console.log("添加分数属性后stu:",stu); // 删除年龄属性 delete stu.age; console.log("删除年龄属性后stu:",stu); // 修改性别属性 stu.sex = "女"; console.log("修改性别属性后stu:",stu); console.groupEnd(); //在ES5中,为对象拓展了一个叫做“特性”的东西,为对象中属性的可访问行进行了限制 如设置属性是否可以被枚举的特性enumerable [ɪ'njʊmərəbl],属性值为布尔值 console.group("ES5限制枚举特性"); // 使用for...in 枚举(一一列举)对象的属性 for(var i in stu){ console.log(i);//没有限制枚举的情况下,正常获取对象的每一个属性 } // console.log("====== 开始限制某个独立属性的枚举 ======") // // 使用ES5中对象的特性 enumerable限制枚举 属性值设置为false则对象属性不可枚举 默认true可枚举 // Object.defineProperty(stu,"score",{ // // 设置score属性不可枚举 // enumerable:false // }) // for(var i in stu){ // console.log(i);//限制枚举的情况下,不能正常获取对象限制枚举的属性 // } console.log("====== 限制多个属性的枚举 ======") Object.defineProperties(stu,{ "name":{ enumerable:false }, "sex":{ enumerable:false } }) for(var i in stu){ console.log(i);//限制枚举的情况下,不能正常获取对象限制枚举的属性 } console.groupEnd();
总结:对象中的属性默认可以被枚举;但使用ES5方法限制枚举后,被限制枚举后的属性就不可以再次枚举了
JS中的三类对象:
内置对象(native object)是由ECMAScript规范定义的对象或者类。例如:函数对象、数组对象、日期对象、正则表达式对象等等
宿主对象(host object) 是由js编译器所嵌入的宿主环境(web浏览器)所定义的。比如客户端js中表示网页结构的HTMLElement对象就是宿主环境创建的对象。宿主环境定义的对象可以直接使用的话,我们也可以把它们当做内置对象。
自定义对象(user-defined object) 由运行中的js创建的对象。
JS中的两类属性:
自有属性(own property) 直接在对象当中定义的属性,区别于继承属性。
继承属性(inherited property) 在对象原型中定义的属性。
JS中属性描述符对象(属性的特性描述):
ES5中定义了一个属性描述符对象(property descriptor)。这个对象属性和他们所描述的属性特性是同名的。
value:属性的值。
writable:可写性,是否可以设置值。
enumerable:可枚举性,遍历对象时该属性会不会出现。
configurable:可配置性。
通过value特性配置值:
// 在ES3.1中,只要对象能够访问到,就可以任意的去操作该对象,访问对象、添加属性、修改属性和删除属性,如下: // console.group("ES3对象操作"); // var stu = { // name:"张三", // sex:"男", // age:23 // } // console.log("原对象stu:",stu); // // 添加分数属性 // stu.score = 100; // console.log("添加分数属性后stu:",stu); // // 删除年龄属性 // delete stu.age; // console.log("删除年龄属性后stu:",stu); // // 修改性别属性 // stu.sex = "女"; // console.log("修改性别属性后stu:",stu); // console.groupEnd(); //在ES5中,为对象的属性配置值 console.group("ES5添加属性的值"); // 创建一个空对象 var stu = {} // ES5方法为空对象添加属性和属性值 Object.defineProperty(stu,'name',{ // 配置值 添加属性值 value:"张翠花" }) // 访问对象 通过这种方式添加的属性 console.log("stu:",stu);//stu: {name: "张翠花"} // 删除属性 不能使用 delete 删除配置的属性 delete stu.name; console.log("stu:",stu);//stu: {name: "张翠花"} // 访问属性 可以访问配置的属性 console.log("stu.name:",stu.name);//stu.name: 张翠花 // 修改属性 不可以使用 obj.att 或 obj[att]方 式修改属性 stu.name = "张无忌"; // 访问属性 可以访问配置的属性 console.log("stu.name:",stu.name);//stu.name: 张翠花 //枚举属性 不可以被枚举 for(var i in stu){ console.log(i); } console.groupEnd(); // ES5,修改对象属性 var obj = {type:"未知"}; console.log(obj.type);//未知 Object.defineProperty(obj,"type",{ value:"狗" }); console.log(obj.type);//狗 obj.type = "猫"; console.log(obj.type);//猫 delete obj.type; console.log(obj.type);//undefined /* * 总结:1. 当一个对象中没有任何原有属性,通过特性的方式 Object.definePeoperty方式添加的属性,里面属性的所有特性都是false: * 即:通过这种方式添加的属性不能够删除、修改和枚举。 * 2. 如果一个对象中天生自带某个属性,此时对象属性的所有特性属性值为true: * 即:只要对象能访问到,不管什么方式,都可以进行任意的操作 */
通过writable特性配置可读写性
// 创建一个对象 var obj = {type:"未知"} // 将对象的属性type设置为不可写特性(即:不可修改) Object.defineProperty(obj,"type",{ // 设置属性是否可写 true默认可写 false不可写 writable:false }) console.log("修改前属性type:",obj.type);//修改前属性type: 未知 // 尝试使用普通的方式进行修改 结果不可修改 obj.type = "猫"; console.log("修改后属性type:",obj.type);//修改后属性type: 未知
通过enumerable配置可枚举性
// 创建一个对象 var obj = { type:"未知", color:"白色" } // 默认情况下,对象的属性可以被枚举 for(var i in obj){ console.log(i); } // 设置属性color特性 不可枚举 Object.defineProperty(obj,"color",{ // 默认true可被枚举 设置false不可被枚举 enumerable:false }) // 设置color不可枚举后,尝试枚举,color不再出现 for(var i in obj){ console.log(i); }
通过 configurable配置相关配置
// 创建一个对象 var obj = { type:"未知", color:"白色" } console.log(obj.color) // 设置属性特性 配置 value值 writeable可写性 Object.defineProperty(obj,"color",{ // 配置属性的value值 value:"黄色", // 不可修改 writable:false, // 设置是否重复配置相关特性 默认false可重复配置,设置true不可以重复配置 configurable:false }) console.log(obj.color);//白色 // 重复修改 可写特性 的值 Object.defineProperty(obj,"color",{ // 配置属性的value值 value:"黄色", // 可修改 writable:true }) console.log(obj.color);//黄色 // 修改obj中的属性 obj.color = "黑色"; console.log(obj.color);//如果 writeable为true,则可以通过普通方式修改属性值;否则不可以 // 总结:设置属性特性configurable为false的情况下,不能重复配置相关特性,否则报错 TypeError: Cannot redefine property: color
存值器set和取值器get
单项数据绑定:通过修改JS中的数据,页面呈现最新修改的数据信息。这是一种映射关系,把JS数据的变化映射到DOM结构上
数据单项绑定呈现位置
对象所有特性相关应用:
// 创建一个对象 var obj = { name:"张三", age:25 } // 设置多属性特性value Object.defineProperties(obj,{ // 设置属性name的特性 name:{ // 配置值 设置或修改值 value:"张三丰", // 是否可写配置 设置值为false后不可以再通过普通方式修改值 writable:false, // 是否可枚举配置 设置值为false后 不可以枚举属性 enumerable:false, // 是否可配置设置,设置为false后不能使用任何方式重复修改设置 configurable:false }, // 设置属性age的特性 age:{ // 存值器 set:function(value){ this.oAge = value; }, // 取值器 get:function(){ return this.oAge; }, // 设置是否可枚举 设置false后,age属性不再被获取,只显示备用属性 enumerable:false, // 注意:一旦设置set/get方法就是设置了设置值或修改值的配置, // 一旦设置了set特性方法,就不能再设置相同功能的特性 writeable 与 value,否则配置冲突报错 // 设置是否可写 // writable:false, // 配置新的值 // value:"张君宝" } }) // 尝试修改obj中的属性 obj.name = "张翠华"; obj.age = 23; console.log(obj) // 尝试枚举 for(var i in obj){ console.log(i,obj[i]) }
3.2 原型拓展
ES5为原型拓展了几个方法:
1. isPrototypeOf(obj) 方法用于判断原型对象是否是参数实例对象的原型
参数 实例化对象
注 在查找的过程中,会查找整个原型链;即 原型的原型 也是 实例化对象的原型
2. Object.setPrototypeOf(obj,prototype) 方法用于设置某个实例对象的原型
参数 obj 实例化对象
prototype 要给前面实例化对象设置的新原型对象(可以是null,也可以是一个对象)
3. Object.getPrototypeOf(obj)
参数 实例化对象
ES5之前,实例化对象通过 proto 属性来获取原型对象;
ES5中,不推荐以 __ 开头的语句,所以提供了 getPropertyOf() 方法用于获取对象的原型对象
3.3 取消对象可拓展性
Object.preventExtensions(obj) 方法用于取消对象的可拓展性
Object.isExtensible(obj) 判断对象是否取消了可拓展性
返回值 一个布尔值,返回true对象可以拓展属性 返回false对象不可拓展属性
注:当一个对象被取消了可拓展性之后,对象不能再拓展属性,但是可以修改和删除属性
var obj = { a:1, b:2 } console.log("冻结前对象:",obj); // 取消对象拓展 Object.preventExtensions(obj); // 拓展属性 obj.c = 3; // 删除属性 delete obj.a; // 修改属性值 obj.b = 22; // 判断对象是否可拓展 console.log(Object.isExtensible(obj));//true console.log("冻结后并操作属性后的对象:",obj);
3.4 封闭对象
Object.seal(obj) 封闭对象的属性
Object.isSealed(obj) 判断对象属性是否被封闭返回值布尔值,返回true 对象被封闭 返回false对象没有被封闭
注:当一个对象被封闭后,不能拓展和删除属性,但是可以修改属性。
var obj = { a:1, b:2 } console.log("封闭前对象:",obj); // 封闭对象 Object.seal(obj); // 拓展属性 obj.c = 3; // 删除属性 delete obj.a; // 修改属性值 obj.b = 22; // 判断对象是否被封闭 console.log(Object.isSealed(obj));//true console.log("封闭后并操作属性后的对象:",obj);
3.5 冻结对象
Object.freeze(obj) 方法用于冻结对象的属性
Object.isFrozen(obj) 方法用于判断对象属性是否被冻结
返回值 一个布尔值 返回true对象被冻结,返回false对象没有被冻结
注:当一个对象被冻结后,不能拓展、修改和删除对象的属性;
var obj = { a:1, b:2 } console.log("冻结前对象:",obj); // 冻结对象 Object.freeze(obj); // 拓展属性 obj.c = 3; // 删除属性 delete obj.a; // 修改属性值 obj.b = 22; // 判断对象是否被冻结 console.log(Object.isFrozen(obj));//true console.log("冻结后并操作属性后的对象:",obj);
3.6 Object.create() 创建对象的新方式
Object.create(proto,[ propertiesObject ]) 方法使用现有对象作为新创建的对象的原型来创建新对象。
参数
proto 该对象应该是新创建对象的原型。可以是null
propertiesObject 可选的。指定要添加到新创建的对象的属性描述符,以及相应的属性名称。这些属性对应于的第二个参数Object.defineProperties()对象属性的特性。
返回值 具有指定原型对象和属性的新对象。
// 学过的创建对象的方式 // var obj = {}; // var obj1 = new Object(); // var obj2 = Object(); // ES5新增创建一个空对象 第一个参数新创建对象的原型设置为null // var obj3 = Object.create(null); // console.log(obj3) //使用Object.create() 来创建一个对象,第一个参数原型对象为一个常量对象 // var obj = Object.create({ // sayHi:function(){ // console.log("Hello"); // } // }) // 第一个参数为空,第二个参数为 要创建对象的空对象的属性特性描述(类似于Object.defineProperty()设置的对象特性) // var obj = Object.create(null,{ // name:{ // // 配置值 // value:"张三", // // 配置是否可写 // writable:false, // // 配置是否可枚举 // enumerable:false // }, // age:{ // // 配置值 // value:10, // // 配置是否可写 // writable:false, // } // }) // console.log(obj); // console.log(obj.name); // // 通过这种对象特性的方式创建的对象,默认属性不能被删除 修改 // obj.name = "haha"; // delete obj.age; // console.log(obj); // console.log(obj.name); // 创建一个对象,并能继承另外一个对象的方法;将一个对象作为另外一个对象的原型 // 创建需要的原型对象 var prototype = { sayHi:function(){ console.log("Hello"); } } // 创建需要的特性属性对象 var options = { name:{ // 配置值 value:"张三", // 配置是否可写 writable:false, // 配置是否可枚举 enumerable:false }, age:{ // 配置值 value:10, // 配置是否可写 writable:false, } } // 两者组合创建对象 var obj = Object.create(prototype,options); console.log(obj)//查看原型 obj.sayHi(); 利用Object.create()完善继承 // 定义父类 function People(name,sex,age){ this.name = name; this.sex = sex; this.age = age; } // 原型中定义方法 People.prototype.sayHi = function(){ return "姓名:" + this.name + ",性别:" + this.sex + ",年龄:" + this.age; } People.prototype.sayHello = function(){ return "Hello"; } // 定义子类 function Student(name,sex,age,score){ // applay实现继承(改变调用对象 People.apply(this,arguments); // 定义子类拓展的属性 this.score = score; } // var p = new People(); // delete p.name; // delete p.sex; // delete p.age; // 子类继承父类中的方法 必须要使用原型继承 将子类的原型指向父类的实例 // Student.prototype = new Student(); // 使用Object.create()优化继承 Student.prototype = Object.create(People.prototype); // 原型继承会造成结构的紊乱,将原型对象的构造函数手动改回到Student Student.prototype.constructor = Student; // 实例化对象 var s = new Student("张三","男",23,100); // 调用父类原型中的方法 console.log(s.sayHi()); /* * 原型继承,子类的原型就是父类的实例,这种方式会在子类的原型中多出几个无用的属性 * 此时,会在子类的原型中多出几个属性:name:undefined,age:undifined,sex:undefined * 如果不考虑寄生组合继承这种方式进行优化,ES5还提供了Object.create()方法来优化 **/
自己封装实现Object.create()方法
// 定义父类 function People(name,sex,age){ this.name = name; this.sex = sex; this.age = age; } // 原型中定义方法 People.prototype.sayHi = function(){ return "姓名:" + this.name + ",性别:" + this.sex + ",年龄:" + this.age; } People.prototype.sayHello = function(){ return "Hello"; } // 定义子类 function Student(name,sex,age,score){ // applay实现继承(改变调用对象 People.apply(this,arguments); // 定义子类拓展的属性 this.score = score; } // 取消Object.create方法 Object.create = null; // 重新自定义 create 方法,实现相同的功能 Object.create = function(prototype){ // 定义一个构造函数 var F = function(){ } // 将F的原型指向传入的原型 F.prototype = prototype; // 返回F的实例 return new F(); } // 使用Object.create方法实现继承 Student.prototype = Object.create(People.prototype); // 实例化对象 var s = new Student("张三","男",23,100); console.log(s.sayHi());