ES5 新特性一览表

概述

ECMAScript 5.1就是我们常说的es5。它在2012年就已经被公开。时至今日,除了一些较低版本的浏览器,各大主流浏览器都已经实现支持了es5的绝大部分特性。
数字化管理平台
Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus
Vue权限系统案例
个人博客地址
先上一段代码,体会一下:

<body>
<h2>单项数据绑定:通过修改JS中的数据,页面呈现最新修改的数据信息。这是一种映射关系,把JS数据的变化映射到DOM结构上</h2>
<div id="box">数据单项绑定呈现位置</div>
<input type="text" id="ipt">
</body>
<script src="./js/jquery-1.12.3.min.js"></script>
<script>
    // 创建对象
    // var obj = {
    
    
    //     des:"把Model的变化映射到DOM结构上"
    // }

    // 获取元素
    // var box = document.getElementById("box");

    // // 设置元素的内部文本
    // box.innerHTML = obj.des;

    // 普通的方式,修改对象的值,查看页面DOM数据变化
    // obj.des = "新的数据";//页面不会发生改变,除非通过innerHTML重新写入

    // 通过set方法设置单项绑定
    // Object.defineProperty(obj,'des',{
    
    
    //     //赋值器
    //     set:function(value){
    
    
    //         // 当设置或修改新的值时,将最新的值输出到页面
    //         box.innerHTML = value;
    //         // 备用属性赋值
    //         this.oDes = value;
    //     },
    //     // 取值器
    //     get:function(){
    
    
    //         return this.oDes;
    //     }
    // });

    // 再次修改以下值试试
    // obj.des = "新的值";//发现页面内容更新了


    // 现在我们做这样一个修改,页面添加一个输入框,随着输入框内容的输入,上面呈现输入框中的内容,修改如下:
    // 创建对象
    var obj = {
    
    
        des:"把Model的变化映射到DOM结构上"
    }

    // 获取元素
    var box = document.getElementById("box");
    var ipt = document.getElementById("ipt");

    // 设置元素的内部文本
    box.innerHTML = obj.des;
    Object.defineProperty(obj,'des',{
    
    
        //赋值器
        set:function(value){
    
    
            // 当设置或修改新的值时,将最新的值输出到页面
            box.innerHTML = value;
            // 备用属性赋值
            this.oDes = value;
        },
        // 取值器
        get:function(){
    
    
            return this.oDes;
        }
    });

    // 新的值来自于页面输入框 触发正在输入事件
    // ipt.oninput = function(){
    
    
    //     if(this.value === ""){
    
    
    //         box.innerHTML = "把Model的变化映射到DOM结构上";
    //     }else{
    
    
    //         obj.des = this.value;
    //     }
    // }
    
    // 含有输入法时,不能检测中文,改造并使用jQuery(使用js也可)如下:

    /**
    * 拓展:实现中文输入法下,仅在选词后触发input事件
    * 描述:在使用oninput监控输入框内容变化时,我们期望仅在value值变化时,才触发oninput事件,而在中文输入下,未选词时的按键也会触发oninput事件。
    * 两个事件:
    *   compositionstart事件
    *   compositionend事件
    * 实现方式:使用一个变量表示拼写状态,在oninput事件中判断是否在拼写状态,当拼写状态结束,继续执行下一步操作。
    * 代码示例如下:
    **/
    // 定义是否开启输入法的开关 默认关闭
    var flag = false;
    //oninput在oncompositionend之前执行,需加定时器
    ipt.oninput = function(){
    
    
        this.addEventListener('compositionstart',function(){
    
    //中文开始书写
            flag = true;//检测到中文输入法开启,设为true
        })
        this.addEventListener('compositionend',function(){
    
    //中文书写完毕,选取文字完毕
            flag = false;//输入完毕,设置false
        })
        setTimeout(function() {
    
    
            if(!flag) {
    
    //保证输入法没有开启也能正常获取数据
                if(ipt.value === ""){
    
    
                    box.innerHTML = "把Model的变化映射到DOM结构上";
                }else{
    
    
                    obj.des = ipt.value;
                }
            }
        },0);
    }
</script>

一、严格模式

1.1 基本概述

除了正常的运行模式(混杂模式),ES5中添加了第二种运行模式:“严格模式”(strict mode)
顾名思义,这种模式,使得JavaScript在更严格的语法下运行
"严格模式"体现了Javascript更合理、更安全、更严谨的发展方向,包括IE 10在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。
另一方面,同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。掌握这些内容,有助于更细致深入地理解Javascript,让你变成一个更好的程序员

1.2 设置严格模式的目的

消除JavaSript语法的一些不合理,不严谨之处,减少一些怪异的行为
消除代码运行的一些不安全之处,为代码的安全运行保驾护航
提高编译器效率,增加运行速度
为未来新版本的JavaScript做好铺垫

1.3 如何使用

在全局 或 函数内部,第一条语句定义为

“use strict”;

如果浏览器不支持,只会解析为一条普通的字符串语句,加以忽略,不会有任何副作用
如果这行语句不在第一行,则无效,整个脚本以"正常模式"运行。

1.4 语法和行为改变

1.4.1 必须使用var声明变量

在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。

a = 100;
//非严格模式下,输出 a: 100
//严格模式下,a没有声明,抛出一个错误 Error: a is not defined
console.log("a:",a);

1.4.2 禁止使用八进制数

var num4 = 076;//八进制标识 0
//非严格模式下,使用八进制没有任何位置
//严格模式下,不支持八进制 报错SyntaxError: Octal literals are not allowed in strict mode.

1.4.3 禁止使用arguments.callee

在函数内部,有两个特殊的对象:arguments 和 this。其中, arguments 的主要用途是保存函数参数,但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。
但在严格模式下,是被禁止使用的。
如下,实现阶乘的递归调用

function factorial(num){
    
    
    if(num == 1){
    
    
        return 1;
    }else{
    
    
        // return num * factorial(num - 1);//原来阶乘方式
        
        //非严格模式下,使用arguments.callee()指向拥有这个arguments对象的函数
        //严格模式下禁止使用 报错TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
        return num * arguments.callee(num - 1);
     }  
}

1.4.4 禁止使用 delete 删除变量

delete常用于删除对象中的属性;如果使用delete删除一个变量,在正常模式下可以执行通过,但不会删除成功;在严格模式下是禁止这样使用的

// 定义变量a
var a = 100;
// 定义window全局对象的变量b
window.b = 200;
// 以上两个都属于全局变量
    
// 删除操作
// 非严格模式下,可以删除window对象的全局变量b,但是不能删除使用var声明的全局变量a,不会报错
// 严格模式下,禁止使用delete删除变量,一旦使用就会报错:SyntaxError: Delete of an unqualified identifier in strict mode
delete a;
delete b;

1.4.5 禁止自定义函数中的this指向window

(function fn(){
    
    
    //非严格模式下,普通函数中的this指向window
    //严格模式下,普通函数中的this不再指向window  返回undefined
	console.log(this);
})();

1.4.6 创建 eval 作用域,即:块级作用域

正常模式下,Javascript语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval作用域。
正常模式下,eval语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval内部使用

eval("var x = 10;");  
 
// 非严格模式下,x为10  
// 严格模式下,x没有声明,抛出一个错误  Error: s is not defined
alert(x); 

任意由eval()创建的变量或函数仍呆在eval()里。然而,你可以通过从eval()中返回一个值的方式实现值的传递:
var result = eval("var a = 10,b = 20;a + b");
// 严格模式与非严格模式下都能正常工作(得到30)
console.log("result:",result);//result: 30

1.4.7 width作用域

with(target){}方法:
正常模式下:如果添加了tatget对象,它会把这个对象,当作with要执行的代码体的作用域的最顶端,也就是with会改变作用域链。
作用域链是经过很复杂的情况生成的结构,作用域链改了之后,系统内核会消耗大量的效率去更改作用域链,是会把程序变得非常慢的。
所以,在ES5严格模式下,with方法就不可以再使用了。

   "use strict";

    var num = 10;
    var obj = {
    
    
        name:"张三",
        num:100
    }
    fn()
    function fn(){
    
    
        var num = 1000;
        // console.log(num);//1000
        // with(window){
    
    
        //     console.log(num);//10
        // }
        with(obj){
    
    
            console.log(num);//100

            var score = 99;

            console.log(score);//99
        }
        console.log(score);//99
    }
    // console.log(score);//Error: score is not defined  width(){}中声明的不存在的变量的作用域由它所在的那个作用域决定

    // 严格模式下不支持 width(){}  报错  SyntaxError: Strict mode code may not include a with statement 

1.4.8 对象不能有重名的属性

正常模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。严格模式下,这属于语法错误(但是程序目前检测不出来)。

var obj = {
    
    a:10,a:100}

1.4.9 函数不能有重名的参数

正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,这属于语法错误。

"use strict";
//严格模式下报错 Error: Duplicate parameter name not allowed in this context
function fn(a,a,b){
    
    
  return;
}

1.4.10 保留字

为了向将来Javascript的新版本过渡,严格模式新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。
使用这些词作为变量名将会报错。

//非严格模式下不会报错
//严格模式下报错 Error: Unexpected strict mode reserved word
var let = 10;

此外,ECMAscript第五版本身还规定了另一些保留字(class, enum, export, extends, import, super),以及各大浏览器自行增加的const保留字,也是不能作为变量名的。

注意:经过测试 IE6,7,8,9 均不支持严格模式

二、提供全局的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

<body>
<h2>单项数据绑定:通过修改JS中的数据,页面呈现最新修改的数据信息。这是一种映射关系,把JS数据的变化映射到DOM结构上</h2>
<div id="box">数据单项绑定呈现位置</div>
<input type="text" id="ipt">
</body>
<script src="./js/jquery-1.12.3.min.js"></script>
<script>
    // 创建对象
    // var obj = {
    
    
    //     des:"把Model的变化映射到DOM结构上"
    // }

    // 获取元素
    // var box = document.getElementById("box");

    // // 设置元素的内部文本
    // box.innerHTML = obj.des;

    // 普通的方式,修改对象的值,查看页面DOM数据变化
    // obj.des = "新的数据";//页面不会发生改变,除非通过innerHTML重新写入

    // 通过set方法设置单项绑定
    // Object.defineProperty(obj,'des',{
    
    
    //     //赋值器
    //     set:function(value){
    
    
    //         // 当设置或修改新的值时,将最新的值输出到页面
    //         box.innerHTML = value;
    //         // 备用属性赋值
    //         this.oDes = value;
    //     },
    //     // 取值器
    //     get:function(){
    
    
    //         return this.oDes;
    //     }
    // });

    // 再次修改以下值试试
    // obj.des = "新的值";//发现页面内容更新了


    // 现在我们做这样一个修改,页面添加一个输入框,随着输入框内容的输入,上面呈现输入框中的内容,修改如下:
    // 创建对象
    var obj = {
    
    
        des:"把Model的变化映射到DOM结构上"
    }

    // 获取元素
    var box = document.getElementById("box");
    var ipt = document.getElementById("ipt");

    // 设置元素的内部文本
    box.innerHTML = obj.des;
    Object.defineProperty(obj,'des',{
    
    
        //赋值器
        set:function(value){
    
    
            // 当设置或修改新的值时,将最新的值输出到页面
            box.innerHTML = value;
            // 备用属性赋值
            this.oDes = value;
        },
        // 取值器
        get:function(){
    
    
            return this.oDes;
        }
    });

    // 新的值来自于页面输入框 触发正在输入事件
    // ipt.oninput = function(){
    
    
    //     if(this.value === ""){
    
    
    //         box.innerHTML = "把Model的变化映射到DOM结构上";
    //     }else{
    
    
    //         obj.des = this.value;
    //     }
    // }
    
    // 含有输入法时,不能检测中文,改造并使用jQuery(使用js也可)如下:

    /**
    * 拓展:实现中文输入法下,仅在选词后触发input事件
    * 描述:在使用oninput监控输入框内容变化时,我们期望仅在value值变化时,才触发oninput事件,而在中文输入下,未选词时的按键也会触发oninput事件。
    * 两个事件:
    *   compositionstart事件
    *   compositionend事件
    * 实现方式:使用一个变量表示拼写状态,在oninput事件中判断是否在拼写状态,当拼写状态结束,继续执行下一步操作。
    * 代码示例如下:
    **/
    // 定义是否开启输入法的开关 默认关闭
    var flag = false;
    //oninput在oncompositionend之前执行,需加定时器
    ipt.oninput = function(){
    
    
        this.addEventListener('compositionstart',function(){
    
    //中文开始书写
            flag = true;//检测到中文输入法开启,设为true
        })
        this.addEventListener('compositionend',function(){
    
    //中文书写完毕,选取文字完毕
            flag = false;//输入完毕,设置false
        })
        setTimeout(function() {
    
    
            if(!flag) {
    
    //保证输入法没有开启也能正常获取数据
                if(ipt.value === ""){
    
    
                    box.innerHTML = "把Model的变化映射到DOM结构上";
                }else{
    
    
                    obj.des = ipt.value;
                }
            }
        },0);
    }
</script>

对象所有特性相关应用:

  // 创建一个对象
    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() 方法用于获取对象的原型对象

<body>
<ul>
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>
</body>
<script>
    // 获取类数组对象
    var lis = document.getElementsByTagName("li");

    // 定义数组
    var arr = [10,20,30,40];

    // 自定义对象
    var obj = {
    
    name:"张三"}

    // 查看Array.prototype这个原型是哪个实例化对象的原型
    console.log(Array.prototype.isPrototypeOf(lis));//false
    console.log(Array.prototype.isPrototypeOf(obj));//false
    console.log(Array.prototype.isPrototypeOf(arr));//true

    // 查找过程中,会查找整个原型链
    console.log(Object.prototype.isPrototypeOf(lis));//true
    console.log(Object.prototype.isPrototypeOf(obj));//true
    console.log(Object.prototype.isPrototypeOf(arr));//true

    // 获取实例化对象 arr 的原型对象
    // 原始方式
    console.log(arr.__proto__);//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
    // ES5方式
    console.log(Object.getPrototypeOf(arr));//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
    // 对比
    console.log(arr.__proto__ === Object.getPrototypeOf(arr));//true

   
    // 设置实例化对象 arr 的原型对象  可以为null 也可以是一个对象  会修改整个原型链
    Object.setPrototypeOf(arr,null);
    console.log(Object.getPrototypeOf(arr));//null

    Object.setPrototypeOf(arr,{
    
    a:10});
    console.log(Object.getPrototypeOf(arr));//{a:10}

    // 构造函数的原型不变  只是实例化对象原型指向改变
    console.log(Array.prototype);//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
</script>

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());

猜你喜欢

转载自blog.csdn.net/qq_39335404/article/details/130480118