JS 高级(四)ES5保护对象、Object.create()、替换this指向

举报
敬 之 发表于 2022/04/15 23:51:08 2022/04/15
【摘要】 目录 ES5(ECMAScript 第5个版本) 1. 保护对象 2. Object.create() 3. 替换this ES5(ECMAScript 第5个版本) 1. 保护对象         在旧的 js 中,对象自己毫无自保能力,所...

目录

ES5(ECMAScript 第5个版本)

1. 保护对象

2. Object.create()

3. 替换this


ES5(ECMAScript 第5个版本)

1. 保护对象

        在旧的 js 中,对象自己毫无自保能力,所以 ES5 中提供了一套保护对象自身的机制。ES5 中,对象中每个属性,不再只是一个简单的值,它的底层已经变成了一个缩微的小对象。

三个开关

writable: true

控制是否可以修改当前属性

enumerable: true

控制是否可以用for in遍历到这个属性(只防for in不防 . )

configurable: true

控制是否可以删除当前属性

控制是否可以修改前两个开关(一旦改为false将不可逆)

修改开关

        只修改一个属性的开关时,格式如下:


  
  1. Object.defineProperty(对象名, "属性名", {
  2. 开关:truefalse,
  3. ... : ...
  4. })

        需要注意,只要修改 writable 和 enumerable 两个开关时,都要同时修改 configurable:false,这样可以阻止别人的程序重新打开之前关闭的开关。

        以上方法每次只能修改一个属性的开关,如果对象中有很多属性都需要保护,则代码会很繁琐,所以有一种新的格式,专门用于修改多个属性的开关:


  
  1. Object.defineProperties(对象名,{
  2. 属性名1:{
  3. 开关名: truefalse,
  4. ... : ...
  5. },
  6. 属性名2:{
  7. 开关名: truefalse,
  8. ... : ...
  9. },
  10. })

举例:使用开关,保护单个对象属性


  
  1. <script>
  2. "use strict";
  3. var eric = {
  4. eid: 1001,
  5. ename: "埃里克",
  6. salary: 12000
  7. }
  8. //员工编号只读
  9. Object.defineProperty(eric, "eid", {
  10. writable: false,
  11. //且不想让别人重新打开writable开关
  12. configurable: false //不可逆!
  13. });
  14. //禁止删除ename属性
  15. Object.defineProperty(eric, "ename", {
  16. configurable: false
  17. })
  18. //禁止随意遍历薪资属性
  19. Object.defineProperty(eric, "salary", {
  20. enumerable: false,
  21. configurable: false //不可逆!
  22. })
  23. </script>

举例:使用开关,保护多个对象属性


  
  1. <script>
  2. "use strict";
  3. var eric = {
  4. eid: 1001,
  5. ename: "埃里克",
  6. salary: 12000
  7. }
  8. //要求:员工编号只读,禁止删除ename属性,禁止随意遍历薪资属性
  9. Object.defineProperties(eric, {
  10. eid: {
  11. writable: false,
  12. configurable: false
  13. },
  14. ename: {
  15. configurable: false
  16. },
  17. salary: {
  18. enumerable: false,
  19. configurable: false
  20. }
  21. })
  22. </script>

访问器属性       

        如果我们想要使用灵活的自定义规则来保护属性值时,以上三个开关是不适用的。所以,访问器属性应运而生。它自己不保存属性值,只提供对另一个数据属性的保护,相当于“保镖”。

定义访问器属性步骤:

(1)定义小黑屋(半隐藏的数据属性),转移原对象中原属性的值;


  
  1. Object.defineProperty(原对象,"小黑屋",{
  2. value:原对象.要保护的属性,
  3. writable:true,
  4. enumerable:false, //小黑屋不能轻易被人发现
  5. configurable:false //小黑屋不可删除
  6. })

(2)定义访问器属性替身,和两个“保镖”;


  
  1. Object.defineProperty(原对象,"要保护的原属性名",{
  2. get:function(){
  3. //this->访问器属性eage所在的当前对象->eric
  4. return this.小黑屋;
  5. },
  6. set:function(value){
  7. if(判断条件){
  8. this.小黑屋=value;
  9. }else{
  10. throw Error("自定义错误提示");
  11. }
  12. },
  13. //访问器属性自己不存值,只提供保护,所以没有value属性
  14. //因为替身必须替真实属性抛头露面,必须可以被for in发现
  15. enumerable:true; (必须设置true)
  16. //因为替身不能随意删除,所以
  17. configurable:false
  18. })

        注意正是因为 writable 不好用,我们才被迫用访问器属性代替 writable,所以用了 get/set 之后,就不再用 writable 开关了。

外界使用访问器属性:

取值 对象.属性名 底层: 自动调用访问器属性的get()
修改 对象.属性名=新值 底层: 自动调用访问器属性的set(),将新值传给()中的value形参

举例:使用访问器属性保护年龄属性


  
  1. <script>
  2. var eric = {
  3. ename: "埃里克",
  4. eage: 25
  5. }
  6. // 要求:年龄可以修改,但必须介于18~65之间
  7. // 1.定义小黑屋属性,转移原对象中原属性的值
  8. Object.defineProperty(eric, "xiaoheiwu", {
  9. value: eric.eage,
  10. writable: true,
  11. enumerable: false, //不可轻易被发现
  12. configurable: false //不可随意删除
  13. })
  14. // 2.定义访问器属性替身
  15. Object.defineProperty(eric, "eage", {
  16. get: function () {
  17. console.log(`eage调用了get(),返回${this.xiaoheiwu}到外部`);
  18. return this.xiaoheiwu;
  19. },
  20. set: function (value) {
  21. if (value >= 18 && value <= 65) {
  22. // 此处this指代当前访问器所在的对象
  23. this.xiaoheiwu = value;
  24. } else {
  25. throw Error("年龄必须介于18~65之间");
  26. }
  27. },
  28. enumerable: true,
  29. configurable: false
  30. })
  31. console.log(eric);
  32. // 外界
  33. // 获得eric的eage值
  34. console.log(eric.eage);//25
  35. // 修改eric的eage值
  36. eric.eage = 60;
  37. console.log(eric.eage);//60
  38. eric.eage = -1;//Uncaught Error: 年龄必须介于18~65之间
  39. </script>

保护结构

三个级别 用法格式 含义
防扩展 Object.preventExtensions(对象)

禁止给对象添加新属性,但防添加不防删除

密封

Object.seal(对象)

禁止添加新属性,禁止删除现有属性,可修改属性值
冻结 Object.freeze(对象) 禁止添加和删除属性,禁止修改一切属性值

        注意点:seal() 会自动遍历对象中每个属性,自动调用 preventExtensions,且自动设置每个属性的 configurable:false,所有属性禁止删除,所以如果用了 seal(),就不用再写 preventExtensions() 和 configurable:false。

        freeze() 会自动遍历对象中每个属性,自动设置每个属性的 configurable:false,所有属性禁止删除。而且会自动遍历对象中每个属性,自动设置每个属性的 writable:false,所有属性只读。

举例:分别使用三个级别保护对象结构;


  
  1. <script>
  2. var eric = {
  3. eid: 1,
  4. ename: "埃里克"
  5. }
  6. // 规定eid只读,ename禁止删除
  7. Object.defineProperties(eric, {
  8. eid: {
  9. writable: false
  10. }
  11. // ename: {
  12. // configurable: false
  13. // }
  14. })
  15. // 禁止给eric添加新属性
  16. // 1.防扩展
  17. // Object.preventExtensions(eric);
  18. // 2.密封(常用)
  19. Object.seal(eric);
  20. // 3.冻结(不常用)
  21. // Object.freeze(eric);
  22. // 试图添加新属性
  23. eric.salary = 20000;
  24. console.log(eric.salary); //该属性为undefined,无法添加
  25. // 试图删除属性eid
  26. delete eric.eid;
  27. console.log(eric); //eid属性依然在,无法删除
  28. // 试图修改eid属性的值
  29. eric.ename = "李湘";
  30. console.log(eric); //用冻结时打印该值仍为埃里克,无法修改
  31. </script>

2. Object.create()

        Object.create() 用于基于一个现有父对象,创建一个新的子对象继承父对象。如果想创建一个子对象,继承父对象,但是又没有构造函数时,就不能用new,而是用 Object.create()。格式:


  
  1. var 新子对象=Object.create(父对象, {
  2. //给子对象添加自有属性
  3. //必须用defineProperties函数相同的格式:
  4. 属性名:{
  5. value:属性值,
  6. writable:true,
  7. enumerable:true,
  8. configurable:false
  9. },
  10. ... : { ... }
  11. })

Object.create() 主要作用为创建新对象、设置新对象继承父对象、强行为新对象添加自有属性。

举例:使用 Object.create() 创建子对象继承父对象;


  
  1. <script>
  2. // 父对象
  3. var father = {
  4. money: 1000000000000,
  5. car: "玛莎拉蒂"
  6. }
  7. // 子对象
  8. var hmm = Object.create(father, {
  9. // 创建自身属性必须用defineProperties函数相同的格式
  10. phone: {
  11. value: "iphone 24",
  12. writable: true,
  13. enumerable: false,
  14. configurable: false
  15. },
  16. bar: {
  17. value: "LV",
  18. writable: true,
  19. enumerable: false,
  20. configurable: false
  21. }
  22. })
  23. console.log(hmm);
  24. console.log(hmm.money, hmm.car) //此时子对象既有父对象的属性,也有自身的属性(继承)
  25. </script>

3. 替换this

        如果系统自动指定的 this 对象不是我们想要的,我们就可主动更换 this 指向的对象。

(1)调用函数时,临时替换一次函数中的 this 为指定对象,使用关键词 call

格式:要调用的函数.call(替换this的对象, 实参值1, 实参值2,...)

举例:使用call,临时替换计算器函数中的 this 为指定员工对象;


  
  1. <script>
  2. // 公用计算器函数
  3. function jisuan(base, bonus1, bonus2) {
  4. console.log(`${this.sname}的总工资为:${base+bonus1+bonus2}`);
  5. }
  6. var lilei = {
  7. sname: "李雷"
  8. }
  9. var hmm = {
  10. sname: "韩梅梅"
  11. }
  12. // 错误写法:
  13. // jisuan(12000, 1000, 1000);
  14. // lilei.jisuan(12000, 1000, 1000);
  15. // 正确写法:
  16. // 要调用的函数.call(替换this的对象, 实参值1, 实参值2,...)
  17. jisuan.call(lilei, 13000, 2000, 520);
  18. jisuan.call(hmm, 1000, 3000, 5000)
  19. </script>

(2)如果多个实参值放在一个数组中,则既需要替换 this,又要拆散数组再传参使用。使用关键字 apply

格式:要调用的函数 .apply(替换this的对象, 包含实参值的数组)

举例:使用 apply 替换 this 同时,拆散数组再传参;


  
  1. <script>
  2. // 公用计算器函数
  3. function jisuan(base, bonus1, bonus2) {
  4. console.log(`${this.sname}的总工资为:${base+bonus1+bonus2}`);
  5. }
  6. var lilei = {
  7. sname: "李雷"
  8. }
  9. var hmm = {
  10. sname: "韩梅梅"
  11. }
  12. var arr = [12000, 1000, 3000];
  13. var arr2 = [13000, 2000, -5000];
  14. // 要调用的函数.apply(替换this的对象, 包含实参值的数组)
  15. jisuan.apply(lilei, arr);
  16. jisuan.apply(hmm, arr2)
  17. </script>

(3)创建函数副本,并永久绑定 this,创建一个和原函数一模一样的新函数,永久替换新函数中的 this 为指定对象。使用关键字 bind

          注意,因为 bind() 已经提前将指定对象替换了新函数中的 this,所以后续每次调用时,不需要再替换 this,除此之外 bind() 不但可以提前永久绑定 this,而且还能永久替换部分形参变量为固定的实参值。

格式:

var 新函数=原函数.bind(替换this的对象)

var 新函数=原函数.bind(替换this的对象, 不变的实参值)

举例:创建lilei专属的jisuan2(),并永久绑定this和底薪;


  
  1. <script>
  2. // 公用计算器函数
  3. function jisuan(base, bonus1, bonus2) {
  4. console.log(`${this.sname}的总工资为:${base+bonus1+bonus2}`);
  5. }
  6. var lilei = {
  7. sname: "李雷"
  8. }
  9. var hmm = {
  10. sname: "韩梅梅"
  11. }
  12. // var 新函数=原函数.bind(替换this的对象, 不变的实参值)
  13. var jisuan2 = jisuan.bind(lilei, 12000);
  14. jisuan2(2000, 3000); //17000
  15. jisuan2(2000, -3000); //11000
  16. // 被bind()永久绑定的this,即使用call也无法再替换为其它对象
  17. jisuan2.call(hmm, 1000, 2000); //仍显示lilei
  18. </script>

替换this知识小结:

a. 只在一次调用函数时,临时替换一次this,使用call

b. 既要替换一次this,又要拆散数组再传参,使用apply
c. 创建新函数副本,并永久绑定this,使用bind

往期JavaScript高级博文链接:

JavaScript高级(一)

JavaScript高级(二)

JavaScript高级(三)

文章来源: majinjian.blog.csdn.net,作者:Developer 小马,版权归原作者所有,如需转载,请联系作者。

原文链接:majinjian.blog.csdn.net/article/details/120046794

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。