zl程序教程

您现在的位置是:首页 >  后端

当前栏目

【JS高级】ES5标准规范之严格模式下的保护对象_09

JS保护对象模式 高级 09 严格 Es5
2023-09-14 09:13:39 时间

目录

❣️ 保护对象

1. 存在问题

2. 解决方案

3. ES5中规定: 每个对象的每个属性也是一个缩微的小对象

4. 如何修改一个属性的开关

5. 存在问题

6. 解决方案

7. 什么是访问器属性

8. 如何创建访问器属性

9. 外界程序如何使用访问器属性

11. 存在问题

12. 如何回答

13. 存在问题

14. 解决方案

💥 扩展:this判断—8种指向

💠 总结:知识点提炼 


 🆙【前文回顾】👉  ES5标准规范之严格模式详解_08


  

❣️ 保护对象

1. 存在问题

旧js中,对象毫无自保能力。外界可以随意修改对象的属性值

2. 解决方案

ES5中为对象的每个属性都提供了自我保护的机制。

3. ES5中规定: 每个对象的每个属性也是一个缩微的小对象

         (1). 每个属性的缩微的小对象中又包含四个特性:

         (2). 如何查看ES5标准下的属性的缩微小对象(了解)

         var 对象=Object.getOwnPropertyDescriptor(对象名, "属性名")

4. 如何修改一个属性的开关

         (1). 错误: 直接用点来访问属性对象内的开关特性:

         eric.eid.writable=false

         (2). 正确: 只能靠专门的函数

           定义  属性

         Object.defineProperty(eric, "eid", {

                  开关名:true或false,

                  ... : ...

         })

         (3). 强调: 在修改writable和enumerable两个开关时,为了防止别人将已经关闭的开关重新打开,都必须同时设置configurable为false作为双保险,禁止修改前两个开关。

         (4). 示例: 使用defineProperty保护对象的属性: ⏬

         1_defineProperty.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    //本例必须在严格模式下才能看到错误!
    "use strict";

    //一个员工对象
    var eric={
      eid:1001,
      ename:"埃里克",
      salary:12000
    }
    //公司规定: 
    //1. 所有员工的编号不能擅自修改
    //2. 禁止删除员工姓名属性
    //3. 禁止for in遍历薪资属性

    //无法获得对象的属性的描述对象,无法看到开关
    // console.log(eric.eid);
    //可以获得eric对象的eid属性的描述对象
    var obj=Object.getOwnPropertyDescriptor(
      eric, "eid"
    )
    console.log(obj);
    //想保护eid属性只读!
    //错误:
    //eric.eid.writable=false;
    //正确: 
    //(重新)定义  属性     eric的eid属性
    Object.defineProperty(eric,"eid",{
      //不可修改
      writable:false,
      configurable:false //禁止修改writable
    })
    //设置ename属性禁止删除
    Object.defineProperty(eric,"ename",{
      configurable:false
    })
    //设置salary属性禁止for in遍历
    Object.defineProperty(eric,"salary",{
      enumerable:false,
      configurable:false
    })
    
    //尝试把eid属性的writable开关重新打开
    // Object.defineProperty(eric,"eid",{
    //   writable:true,
    //   configurable:true
    // })//报错: Cannot redefine property: eid
    //尝试修改员工编号
    //eric.eid=-2;//报错: Cannot assign to read only property 'eid'
    //尝试删除ename属性: 
    //delete eric.ename;//报错: Cannot delete property 'ename'
    //尝试用for in遍历eric的所有属性值,包括薪资
    for(var key in eric){
      console.log(`${key} : ${eric[key]}`)
    }
    console.log(eric);
    //虽然salary不能被for in遍历,但是用.依然可以强行访问!
    //只防for in 不能防点(.)
    console.log(`eric的薪资是:${eric.salary}`)
  </script>
</body>
</html>

运行结果:

{value: 1001, writable: true, enumerable: true, configurable: true}

eid : 1001

ename : 埃里克

{eid: 1001, ename: "埃里克", salary: 12000}

eric的薪资是:12000 

​​​​​​         (5). 问题: 每修改一个属性的开关都要反复写Object.defineProperty(),太繁琐!

         (6). 解决: 可用Object.defineProperties()同时修改多个属性的开关:

         Object.defineProperties(对象名, {

                  属性名:{

                          开关: true或false,

                          ... : ...

                  },

                  ... : { ... }

         })

         (7). 示例: 使用defineProperties简化defineProperty

         2_defineProperties.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    //本例必须在严格模式下才能看到错误!
    "use strict";

    //一个员工对象
    var eric={
      eid:1001,
      ename:"埃里克",
      salary:12000
    }
    //公司规定: 
    //1. 所有员工的编号不能擅自修改
    //2. 禁止删除员工姓名属性
    //3. 禁止for in遍历薪资属性
    Object.defineProperties(eric,{
      eid:{
        writable:false,
        configurable:false
      },
      ename:{
        configurable:false
      },
      salary:{
        enumerable:false,
        configurable:false
      }
    })
    
    
    //尝试把eid属性的writable开关重新打开
    // Object.defineProperty(eric,"eid",{
    //   writable:true,
    //   configurable:true
    // })//报错: Cannot redefine property: eid
    //尝试修改员工编号
    // eric.eid=-2;//报错: Cannot assign to read only property 'eid'
    //尝试删除ename属性: 
    // delete eric.ename;//报错: Cannot delete property 'ename'
    //尝试用for in遍历eric的所有属性值,包括薪资
    for(var key in eric){
      console.log(`${key} : ${eric[key]}`)
    }
    console.log(eric);
    //虽然salary不能被for in遍历,但是用.依然可以强行访问!
    console.log(`eric的薪资是:${eric.salary}`)
  </script>
</body>
</html>

运行结果:

eid : 1001

ename : 埃里克

{eid: 1001, ename: "埃里克", salary: 12000}

eric的薪资是:12000 

5. 存在问题

三个开关都无法利用自定义规则灵活保护属性值!

6. 解决方案

今后,只要希望利用自定义规则保护属性值时,都要用访问器属性

7. 什么是访问器属性

自己不保存属性值,而是只提供对另一个数据属性的保护!——保镖

8. 如何创建访问器属性

访问器属性只能用Object.defineProperty或Object.defineProperties添加,不能用{}或.方式直接添加:

         Object.defineProperties(对象名, {

         (1). 创建一个受保护的实际存储属性值的属性,隐姓埋名,半隐藏

                  受保护的属性名:{

                          value: 原对象中原属性值

                          writable:true,

                          enumerable:false,

                          configurable:false

                  },

         (2). 创建一个访问器属性来,冒名顶替原属性,对受保护的属性提供保护

                  访问器属性名:{

                          //专门负责从受保护的属性中获取当前的属性值

                          get:function(){

                                   return this.受保护的属性

                          },

                          //专门负责将外界修改来的新属性值,先经过验证,再保存到受保护的属性中。

                          set:function(value){

                                   if(value符合要求){

                                            this.受保护的属性=value

                                   }

                          },

                          enumerable:true,

                          configurable:false

                  }

         })

9. 外界程序如何使用访问器属性

用法和使用普通属性完全一样,但是:

         (1). 当外界试图获取访问器属性值时,程序会自动调用访问器属性的get函数,从受保护的属性中,取出当前属性值

         (2). 有人试图修改年龄,程序会自动调用set函数,并将外界修改来的新值自动交给set函数的value属性:

10. 示例: 使用访问器属性保护员工年龄属性: ⏬

3_get_set.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    var eric={
      eid:1001,
      ename:"埃里克",
      eage:25
    }
    Object.defineProperties(eric,{
      //(1). 先添加一个隐姓埋名的实际保存属性值的半隐藏的属性: 
      __eage:{
        //从旧对象eric中取出原属性值eage,保存到新创建的半隐藏属性中
        value:eric.eage,
        writable:true,
        enumerable:false,//半隐藏
        configurable:false//禁止修改前两个特性,也禁止删除
      },
      //(2). 请保镖,创建一个访问器属性,冒名顶替原属性
      eage:{
        //专门负责从受保护的属性中取出属性值给外界
        get:function(){
          console.log(`自动调用get()`);
          return this.__eage;
        },
        //专门负责将外界修改来的新属性值经过验证后,保存到受保护的属性中
        set:function(value){
          console.log(`自动调用set(),形参value自动获得${value}`);
          if(value>=18&&value<=65){
            this.__eage=value;
          }else{
            throw Error("年龄必须介于18~65之间!")
          }
        },
        //替受保护的属性抛头露面
        enumerable:true,
        configurable:false
      }
    })
    //员工年龄可以修改,但是必须介于18~65之间
    //尝试获取eric的eage
    console.log(eric.eage);//自动调用get() 25 
    //尝试修改eric的eage为26
    eric.eage=26;//自动调用set(),value收到26
    console.log(eric.eage);//自动调用get(), 26
    //尝试修改eric的eage为-2
    eric.eage=-2;//自动调用set(), value收到-2,结果报错!
    console.log(eric);
  </script>
</body>
</html>

运行结果:

自动调用get()

25

自动调用set(),形参value自动获得26

自动调用get()

26

自动调用set(),形参value自动获得-2

Uncaught Error: 年龄必须介于18~65之间! 

11. 存在问题

访问器属性的get()和set()方法,明明放在访问器属性的{}内,为什么通过this可以访问到外部对象的属性?

12. 如何回答

其实,访问器属性的get()和set()方法,一进入对象中,就被打散了!直接隶属于对象,不再局限于访问器属性内部

13. 存在问题

开关和访问器属性只能防住对单个属性的修改。防不住修改对象的结构,比如,防不住随意向对象中添加新属性

14. 解决方案

ES5中提供了专门的函数来保护对象的整体结构,分三个级别:

        (1). 防扩展: 禁止给对象随意添加新属性!

               阻止   扩展

         a.  Object.preventExtensions(对象)

         b.  示例: 使用preventExtensions(),阻止给对象添加新属性: ⏬

         4_preventExtensions.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    "use strict";

    var eric={
      eid:1001,
      ename:"埃里克"
    }
    //不但禁止修改eid属性
    Object.defineProperty(eric,"eid",{
      writable:false
    })
    //而且,禁止给eric对象添加任何新属性
    Object.preventExtensions(eric);

    //尝试修改eid属性
    //eric.eid=-2;
    //尝试给eric添加新属性
    //eric.Eid=-2;//报错: Cannot add property Eid, object is not extensible
    console.log(eric);

  </script>
</body>
</html>

运行结果:

Uncaught TypeError: Cannot add property Eid, object is not extensible 

​​​​​​​         c. 问题: 只能防止添加新属性,不能防止删除现有属性。如果用configurable来防止每个属性禁止删除,代码会很繁琐!

        (2). 密封: 即禁止给对象添加新属性,又禁止删除对象中任何现有属性

                         密封

         a. Object.seal(对象)

         b. 原理:

                  1). 代替了preventExtensions()自动禁止添加新属性

                  2). 代替了configurable:false自动设置所有属性的configurable都为false!

         c. 建议: 几乎所有对象都要密封起来!

         d. 虽然对象被密封,不能添加删除属性了,但是属性值还是可以随意修改的!

         e. 示例: 使用seal(),即禁止添加新属性,又禁止删除现有属性 ⏬

         5_seal.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    //本例必须在严格模式下才能看到错误!
    "use strict";

    //一个员工对象
    var eric={
      eid:1001,
      ename:"埃里克",
      salary:12000
    }
    //公司规定: 
    //1. 所有员工的编号只读
    //2. 禁止删除员工姓名属性
    //3. 禁止for in遍历薪资属性
    Object.defineProperties(eric,{
      eid:{
        writable:false
      },
      salary:{
        enumerable:false
      }
    })
    //即禁止给对象添加新属性,又想禁止删除现有属性
    Object.seal(eric);
    
    //尝试给eric添加新属性: 
    eric.Eid=-2;//报错:Cannot add property Eid, object is not extensible
    //尝试把eid属性的writable开关重新打开
    // Object.defineProperty(eric,"eid",{
    //   writable:true,
    //   configurable:true
    // })//报错: Cannot redefine property: eid
    //尝试修改员工编号
    // eric.eid=-2;//报错: Cannot assign to read only property 'eid'
    //尝试删除ename属性: 
    delete eric.ename;//报错: Cannot delete property 'ename'
    //尝试用for in遍历eric的所有属性值,包括薪资
    for(var key in eric){
      console.log(`${key} : ${eric[key]}`)
    }
    console.log(eric);
    //虽然salary不能被for in遍历,但是用.依然可以强行访问!
    console.log(`eric的薪资是:${eric.salary}`)
  </script>
</body>
</html>

运行结果:

4_seal.html:35 Uncaught TypeError: Cannot add property Eid, object is not extensible 

        (3). 冻结: 即禁止添加删除属性,又禁止修改属性值

         a. Object.freeze(对象)

         b. 原理:自动做了三件事:

                  1). 自动调用preventExtensions()防扩展

                  2). 自动修改所有属性的configurable:false,禁止删除

                  3). 自动修改所有属性的writable:false,禁止修改属性值!

         c. 示例: 使用freeze()冻结一个对象的属性和属性值

         6_freeze.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    "use strict";
    var dbconfig={
      host:"127.0.0.1",
      port:3306,
      db:"xz"
    }
    Object.freeze(dbconfig);
    //尝试修改属性值
    //dbconfig.port=5050;//报错:Cannot assign to read only property 'port'
    //尝试添加新属性
    //dbconfig.Port=5050;//报错: Cannot add property Port, object is not extensible
    //尝试删除现有属性: 
    delete dbconfig.port;//报错:Cannot delete property 'port'
  </script>
</body>
</html>

运行结果:

Uncaught TypeError: Cannot delete property 'port' of #<Object>


💥 扩展:this判断—8种指向

this  8种指向: 判断this,一定不要看定义在哪儿!只看调用时!

1. obj.fun()   this->obj

2. fun() 或 (function(){ ... })() 或 多数回调函数 或 定时器函数   this->window

3. new Fun()   this->new正在创建的新对象

4. 类型名.prototype.共有方法=function(){ ... }   this->将来谁调用指谁,同第一种情况

5. DOM或jq中事件处理函数中的this->当前正在触发事件的DOM元素对象

                               如果需要使用简化版函数,必须$(this)

6. 箭头函数中的this->箭头函数外部作用域中的this

7. jQuery.fn.自定义函数=function(){ ... }   this->将来调用这个自定义函数的.前的jQuery子对象,不用再$(this)

8. new Vue()中methods中的函数中的this->当前new Vue()对象


💠 总结:知识点提炼 

1. 只要验证字符串格式或查找、屏蔽敏感词时都要用正则

(1). 最简单的正则: 一个敏感词的原文

(2). 某一位字符上可能有多种备选字时用: [备选字列表]

(3). 如果[]中部分字符是连续的,可用: [x-x]

         a. 一位小写字母: [a-z]

         b. 一位大写字母: [A-Z]

         c. 一位字母(大小写都行): [A-Za-z]

         d. 一位字母或数字都行: [0-9A-Za-z]

         e. 一位汉字: [\u4e00-\u9fa5]

(4). 预定义字符集:

         a.  \d 一位数字

         b.  \w 一位数字、字母或_

         c.  \s  空格、tab、换行等空字符

         d.  .   任意字符

(5). 如果规定一个字符集或子规则反复出现的次数时就用量词:

         a. 有明确数量边界的量词:

      1). {n}  =n 必须n个,不能多也不能少

      2). {n,m}  n个<=    <=m个

      3). {n,}    n个<=   多了不限

         b. 没有明确数量边界的量词:

      1). *   0个<= 可有可无,多了不限

      2). ?   0个或1个  可有可无,最多一个

      3). +   1个<=  至少一个,多个不限

(6). 两个规则中选其一匹配即可: 规则1|规则2

(7).希望将多个子规则分为一组先联合匹配,再和分组外的其他规则联合匹配:

  (多个子规则)

(8). 匹配特殊位置: 3个

         a. 字符串的开头位置: ^

         b. 字符串的结尾位置: $

         c. 英文句子中的单词的左右边界: \b

2. String家提供的正则相关函数: 3件事

(1). 查找敏感词: 4种情况

      a. 查找一个固定的敏感词出现的位置:
         var i=str.indexOf("敏感词")

         // 如果找不到,返回-1

      b. 用正则查找多种敏感词出现的位置:
         var i=str.search(/正则/i)

         // 如果找不到,返回-1

      c. 查找敏感词的内容:

      1). 查找第一个敏感词的内容和位置:
      var arr=str.match(/正则/i)

      // arr: [ 0:"敏感词内容", index:敏感词位置 ]

      // 如果找不到返回null

      2). 查找所有敏感词的内容,不关心位置:
      var arr=str.match(/正则/ig)

      // arr: [ 敏感词1, 敏感词2, ...  ]

      // 如果找不到返回null

     d. 查找每个敏感词的内容和位置: reg.exec

补: js中所有数组底层本质都是关联数组(下标都为字符串)

1. 访问数组中元素值的标注写法:

  arr["下标"]

2. 简写:

  a. 如果下标为自定义字符串名称,可简写为:

  arr.自定义名称的下标

  b. 如果下标为数字内容的字符串,可简写为:

  arr[数字下标]

总结: 查找方法的返回值规律

1. 如果原函数返回的是下标位置i,如果找不到,都返回-1

2. 如果原函数返回的是一个数组arr或一个对象obj,如果找不到,都返回null

3. 如果原函数返回类数组对象,如果找不到返回空类数组对象:

   { length:0 }

(2). 替换敏感词: 2种

         a. 简单替换:
         变量=str.replace(/正则/ig, "新值")

         b. 高级替换:
         变量=str.replace(/正则/ig, function(形参){

               return 根据本次敏感词动态生成一个新值

         })

         c. 删除敏感词:
         变量=str.replace(/正则/ig, "")

(3). 切割字符串:

         a. 简单切割:
         var arr=str.split("切割符")

         b. 复杂切割:
         var arr=str.split(/正则/i)

         c. 打散字符串为字符数组:
         var arr=str.split("")

3. RegExp对象: 

         (1). 创建正则表达式对象:

         a. 如果正则是固定的:
         var reg=/正则/ig

         b. 如果正则需要动态生成:
         var reg=new RegExp("正则",ig)

         (2). 验证字符串格式:
         var bool=reg.test(str)
         reg必须同时前加^后加$

         (3). 既查找每个关键词的内容又查找每个关键词的位置: (待续)

         do{

               var arr=reg.exec(str);

               if(arr!=null){

                              获得本次找到的敏感词的内容(arr[0])和位置(arr.index)

               }

         }while(arr!=null);

4. 函数: 

(1). 创建函数三种方式:

         a. function 函数名(形参列表){ 函数体; return 返回值 } //会被声明提前,不好

         b. var 函数名=function(形参列表){ 函数体; return 返回值 }//不会被声明提前,首选

         c. var 函数名=new Function("形参1", "形参2", ... , "函数体; return 返回值")

函数本质:

  1). 函数也是一个对象,对象中保存着函数的函数体代码

  2). 函数名只是一个普通的变量,函数名通过函数对象地址,引用着函数对象

  3). function在底层等效于new Function()

    function 函数名(){ ... }和var 函数名=function(){}在底层都会被翻译为

    var 函数名=new Function(...)

    只不过function 函数名(){}是先提前,再翻译

    而var 函数名=function(){}是不提前,原地翻译

(2). 重载: 今后,一件事,根据传入不同的参数值,动态执行不同的逻辑时,都用重载

function 一个函数名(不写形参变量){

  //arguments对象自动接住所有实参值

  if(arguments.length==0){

    执行一种逻辑

  }else if(arguments.length==1){

    执行另一种逻辑

  }else{

    执行其它逻辑

  }

}

其中arguments是类数组对象: 和数组相比:

         a. 相同点: 也有下标,length属性,也可for循环遍历

         b. 不同点: 不是数组类型,无法使用数组家的函数

(3). 匿名函数:

         a. 所有回调函数优先使用匿名函数——用完释放,节约内存

         b. 所有js代码都应该保存在匿名函数自调中,禁止使用全局变量,避免全局污染!

         (function(){

               要执行的js代码

         })();

         结果: 匿名函数内的都是局部变量,不会产生全局变量。

         局部变量随匿名函数一起释放。不会污染全局。

(4). 作用域和作用域链: (跟着视频亲自画图!!!)

         a. 作用域:

         1). 全局作用域:window,保存全局变量

         优: 可重用,缺: 随处可用, 极易被污染

         2). 函数作用域: 保存局部变量

         局部变量包括2中: 函数中var出的变量和形参变量

         优: 仅函数内可用,不会被污染,缺: 不可重用

         3). 函数作用域对象原理:

               i. 每个函数定义时都自带好友列表,好友列表里2个格子,一个是空,一个引用window

               ii. 调用函数时临时创建函数作用域对象保存函数局部变量。并将函数作用域对象的地址保存到函数好友列表中离自己近的格子里。

               iii. 函数执行过程中按就近原则先在自己的函数作用域对象中找局部变量使用。如果找不到,才被迫去全局window中找变量使用.

               iv. 函数调用后,好友列表中离自己近的格子清空,导致函数作用域对象以及内部的局部变量被释放!——所以局部变量不可重用!

         b. 作用域链: 保存一个函数所有可用的作用域对象的链式结构(好友列表)学名就叫作用域链。
         1). 作用域链保存着一个函数可用的所有变量

         2). 作用域链控制着变量的使用顺序。先局部后全局。

5. 闭包:  
         a. 只要希望给一个函数保护一个可反复使用的专属变量,又防止这个变量被外界篡改时,都用闭包。

         b. 闭包三步:

      1). 用外层函数妈妈包裹要保护的变量和内层函数

      2). 外层函数妈妈用return把内层函数孩子返回到外部

      3). 外部想使用内层函数的人,必须调用外层函数,才能获得return出来的内层函数对象。并将内层函数保存在一个变量中反复使用。

         c. 闭包形成的原因: 外层函数调用后,外层函数的作用域对象被内层函数引用着无法释放,形成了闭包对象

         d. 闭包的缺点: 闭包比一般的函数占用多一块内存——外层函数的函数作用域对象。所以,用完闭包后,应该尽快释放:
      保存内层函数的变量=null

6. 面向对象: 封装 继承 多态

(1). 封装: 3种:

         a. 用{}创建一个对象:

         var 对象名={

               属性名:属性值,

               ... : ... ,

               方法名: function(){

                              ... this.属性名 ...

               }

         }

         b. 用new Object():

         1). 2步:

               i. var 对象名=new Object()

               ii. 对象名.属性名=属性值;

                               对象名.方法名=function(){ ... }

         2). 对象底层也是关联数组:

               i. 都是名值对儿的集合

               ii. 都可用[""]和.方式访问成员。

                               如果属性名来自于变量,就只能用[],不要加""

               iii. 访问不存在的属性,都不报错,返回undefined

                               判断是否包含某个属性:

                           对象.属性名!==undefined

               iv. 强行给不存在的属性赋值,都不报错,而是自动添加该属性

                               给对象添加新属性,唯一办法,强行赋值:

                           对象名.新属性名=新值

               v. 都可用for in遍历

         c. 只要反复创建多个相同结构的对象都用构造函数:

         1). 2步:

               i. 定义构造函数:

               function 类型名(形参1,形参2, ...){

                              this.属性名1=形参1;

                              this.属性名2=形参2;

                              //构造函数中不要再包含方法定义定义!

               }

               ii. 用new 调用构造函数:

               var 对象名=new 类型名(属性值1, 属性值2,...)

         2). new做了4件事:

               i. 创建一个新的空对象

               ii. 让新对象继承(_ _proto_ _)构造函数的原型对象

               iii. 调用构造函数,传入实参,并自动替换构造函数中的this为new正在创建的新对象。构造函数中,通过强行赋值的方式为新对象添加规定的属性,并保存属性值。

               iv. 返回新对象的地址,保存到=左边的变量中。

        3). 优点: 重用对象结构代码

        4). 缺点: 如果构造函数中包含方法定义,则每次创建新对象都会重复创建相同方法的副本。             ——浪费内存!

(2). 继承: 

         a. 今后,只要同一类型所有子对象共用的方法和属性值,都要集中保存在构造函数的原型对象中!

      构造函数.prototype.属性名/共有方法名=属性值/function(){ ... }

         b. 自有属性和共有属性:

        1). 获取属性值:都可用"子对象.属性名"

        2). 修改属性值:

               i. 自有属性: 子对象.自有属性名=新值

               ii. 共有属性: 构造函数.prototype.共有属性名=新值

        c. 内置类型原型对象:

      1). 11种内置类型/对象: String, Number, Boolean, Array, Date, RegExp, Math(对象), Error, Function, Object, global(对象)

        2). 一种类型=构造函数+原型对象

               i. 构造函数: 创建子对象

               ii. 原型对象: 为所有子对象保存共有成员

        3). 查看该类型共有哪些API: 类型名.prototype

        4). 该类型缺少想用的方法: 类型名.prototype.共有新方法=function(){ ... }

        d. 原型链: 保存着一个对象可用的所有属性和方法。控制着属性和方法的使用顺序:先自有再共有——就近原则!

(3). 多态 

重点讲重写:如果子对象觉得从父对象继承来的成员不好用,可以在子对象自己内部重写和父对象同名的成员,覆盖父对象的成员,优先使用自己的。

******面向对象终极总结: 封装,继承,多态******

①封装: 创建对象,2种:

  如果只创建一个对象: {}

  如果反复创建多个相同结构的对象: 构造函数

②继承: 所有子对象共用的属性值和方法,都要放在构造函数的原型对象中

③多态: 重写: 只要觉得从父对象继承来的成员不要用,都在子对象中重写同名成员

④如果觉得这个父对象对象都不好用,可以自定义继承: 2种:

  1). 只换一个子对象的父对象: 2种:

  i. 子对象.__proto__=新父对象

  ii. Object.setPrototypeOf(子对象, 新父对象)

  2). 更换多个子对象的原型对象: 构造函数.prototype=新对象

********************************************************************

7. 严格模式: "use strict"; 

(1). 禁止给未声明过的变量赋值

(2). 静默失败升级为错误

(3). 普通函数调用中的this不指window,而是指undefined

(4). 禁用arguments.callee

总结: this 判断this时,一定不要看他定义在哪儿。必须看它在哪里以何种方式调用 4种:

1. obj.fun()  this->点前的obj对象

2. fun()  this->默认指window

3. new Fun()   this->new正在创建的新对象

4. 类型名.prototype.共有方法=function(){ ... } 

         this->将来谁调用这个函数,就指谁

         将来调用这个函数的.前的某个子对象

8. 保护对象: ⏬

(1). 保护属性:

         a. 每个属性包含三个开关:

      1). writable: 控制是否可修改属性值

      2). enumerable: 控制着是否可被for in遍历到,但是只防for in不防.

      3). configurable: 控制

               i. 是否可删除当前属性

               ii. 是否可修改writable和enumerable两个开关

                      强调: configurable一旦改为 false,不可逆!

         b. 只修改一个属性的多个开关:

      Object.defineProperty(对象名, "属性名",{开关: true/false})

         c. 修改多个属性的多个开关:

      Object.defineProperties(对象名,{

                      属性名:{ 开关:true/false, ... },

                      ... : ...

      })

         d. 如果用自定义的规则保护属性时,只能用访问器属性: 2步:

      Object.defineProperties(对象,{

      //1). 先定义一个隐姓埋名且半隐藏的数据属性:

               _属性名:{

                              value: 属性的初始值,

                              writable:true,

                              enumerable:false,

                              configurable:false

               },

      //2). 再定义访问器属性保镖冒名顶替要保护的属性

               属性名:{

                              get:function(){

                            return this._属性名

                              },

                              set:function(value){ //value ← 要修改的新属性值

                            先验证value

                            如果验证通过,this._属性名=value

                            否则如果验证未通过,不但不保存新属性值,还会报错

                              },

                              enumerable:true,

                              configurable:false

               }

         })

         外界使用访问器属性时和使用普通属性一样:

         对象.属性名

         外界试图获取访问器属性值时,自动调用get()

         外界试图修改访问器属性值时,自动调用set()

(2). 保护结构: 3个级别

         a. 防扩展: Object.preventExtensions(对象)

         b. 密封: Object.seal(对象)

         c. 冻结: Object.freeze(对象)


🆕【后文传送门】👉  ES5标准规范之创建子对象以及替换this_10


​​​​

如果这篇【文章】有帮助到你,希望可以给【青春木鱼】点个👍,创作不易,相比官方的陈述,我更喜欢用【通俗易懂】的文笔去讲解每一个知识点,如果有对【前端技术】感兴趣的小可爱,也欢迎关注❤️❤️❤️青春木鱼❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💕💕!