0


JavaScript 高级程序设计 - 第 8 章 理解对象 学习笔记

JavaScript 高级程序设计 - 第 8 章 理解对象 学习笔记

本章内容量挺大的,因此笔记的话我也会分成 3-4 个部分去写,想要理解的细一点,顺便之后回顾的时候不会看的套类。

本章主要就是了解一下和理解一下什么是对象,包括 Object 上的 properties 和 mixin。

在 JavaScript 中,所有的存在(除了 primitive value)之外,都是对象,通常意义上来说,创建对象的方式有两种:

const person =newObject();
person.name ='John';
person.age =28;
person.sayHi=function(){
  console.log(`Hello from ${this.name}`);};const person2 ={name:'Alex',age:18,sayHi:function(){
    console.log(`Hello from ${this.name}`);},};

二者在实现上没有什么区别,只是后者使用 object literal(对象字面量)更加的方便简洁。

attributes of property

以上面的实现为例,

name

,

age

sayHi

person

的 数据属性(data properties),并且同样也可以使用

Object.defineProperty()

的方法去赋值,如:

const person2 ={name:'Alex',age:18,sayHi:function(){
    console.log(`Hello from ${this.name}`);},};

Object.defineProperty(person2,'greet',{value:function(){
    console.log('Hello from the other side');},});

person2.greet();

在这里插入图片描述

defineProperty

中传的第三个值,就是该 property 的 attributes。

interfacePropertyDescriptor{
    configurable?:boolean;
    enumerable?:boolean;
    value?:any;
    writable?:boolean;
    get?():any;
    set?(v:any):void;}/**
 * Marker for contextual 'this' type
 */interfaceThisType<T>{}defineProperty<T>(o:T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>):T;
PropertyDescriptor

中包含的,就是所有可能存在的属性,现在的规范是使用中括号括起来,如

[[Configurable]]

,不过说的都是一个东西。

  • valuevalue 为 property 实际的值
  • writableperson2.name ='John';Object.defineProperty(person2,'name',{writable:false,});console.log(person2.name);person2.name ='Dick';console.log(person2.name);Object.defineProperty(person2,'name',{writable:true,value:'Rewritten',});console.log(person.name);结果如下:在这里插入图片描述可以看到,当 writable 设置为 false 之后,重新对 property 赋值也不无法对其进行修改。就算是之后重新修改 writable 也不行非严格模式下这样的执行会被忽略,严格模式下这么做会报错
  • configurableconfigurable 是一个 boolean,当一个属性的 configurable 被设置成 false,意味着当前的属性无法被使用 defineProperty 被重新定义、无法被删除、无法修改 configurable, enumerablewritable。如果 writable 的值为 true,那么 value 还是可以被修改的,否则这个对象就被彻底 freeze,再也无法修改。Object.defineProperty(person2,'age',{configurable:false,writable:false,});Object.defineProperty(person2,'age',{enumerable:false,});在这里插入图片描述顺便,Object.freeze() 等同于将 configurablewritable 设为 false。
  • enumerable这个 attribute 代表了当前属性是否会出现在 for...infor(const key in person2){ console.log(key);}Object.defineProperty(person2,'name',{enumerable:false,});for(const key in person2){ console.log(key);}在这里插入图片描述
  • getgetter 是另一个相对而言比较新的内容,它主要的目的就是为了处理一些返回值,比如:const person2 ={_id:1,name:'Alex',age:18,sayHi:function(){ console.log(`Hello from ${this.name}`);},getid(){return`id is: ${this._id}`;},};console.log(person2.id);在这里插入图片描述这个案例中,person2 没有直接声明 id property,但是可以通过 getter 返回一个 id property,其原因在于,它的具体实现为:const person =newObject();person.name ='John';person.age =28;person._id =2;person.sayHi=function(){ console.log(`Hello from ${this.name}`);};Object.defineProperty(person,'id',{get:function(){returnthis._id;},});console.log(person.id);在这里插入图片描述使用 getter 还有另一个原因,放到下面 setter 一起说。
  • setsetter 的作用和 getter 相似,其用法如下:const person2 ={_id:1,name:'Alex',age:18,sayHi:function(){ console.log(`Hello from ${this.name}`);},getid(){return`id is: ${this._id}`;},/** * @param {string} arg */setrealName(arg){this.name ='my name is: '+ arg;},};在这里插入图片描述同样也可以用 defineProperty 去实现。JS 中出现 getter 和 setter 的另一个原因就在于,ES6 之前 JS 中是没有私有属性的。于是社区约定俗成了一个规范,就是在 property name 中添加 _ 代表不应该被访问的私有属性,要去访问和修改这个私有属性,就需要通过 getter 和 setter 去实现。这样实际保存值的 property,即 this.__privateValue,就不会直接被用户所访问。

定义的方法为

defineProperty

,获取 properties 的方法有两个:

Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptors()

。前者需要知道 property 的名称,后者则会直接返回当前对象上的 properties。

console.log(Object.getOwnPropertyDescriptor(person2,'name'));
console.log(Object.getOwnPropertyDescriptors(person2));

在这里插入图片描述

mixin

mixin 是一个被提到很多次的 concept,如果找一下的话,会发现 vue、scss 等都会发现直接有 mixin 这一属性或是函数。

ES6 也提供了

Object.assign()

的静态方法,它会遍历接收到的参数,然后将参数中所有可枚举 (

Object.propertyIsEnumerable()

true

的 properties) 和 自由(

Object.hasOwnProperty()

true

的 properties) 复制到目标属性上。

这可以很方便的实现 mixin,以下面代码为例:

const writeFile ={write:function(){
    console.log('Assume I can read file.');},};const scanFile ={scan:function(){
    console.log('Assume I can scan file');},};const readWriter = Object.assign({}, readFile, writeFile);
console.log(readWriter);

readFile.read();const readWriterScanner = Object.assign({}, readWriter, scanFile);
readWriterScanner.scan();

在这里插入图片描述

可以看到,使用 mixin 提供更加灵活的语法,并且直接地满足了 SOLID 中的 single responsibility principle 和 interface segregation principle 两条原则。

liskov substitution principle 的判定我觉得稍微有点麻烦,逻辑上来说它要满足的事子类和父类,实现上来说

Object.assign()

完成了 shallow copy……

因为 JS 没有办法提供很好的静态检查,因此当使用 mixin 还会有其他的一些问题,如属性被重写的问题,依旧用上面的分代码为例,这里添加一点新的实现:

const fileOperator ={read:function(){
    console.log('I read file randomly.');},scan:function(){thrownewError('scan file fail.');},};const errorFileOperator = Object.assign({}, scanFile, fileOperator);
errorFileOperator.scan();

可以看到,在这个 mixin 中,

scan

fileOperator

重写了,所以这里的输出结果不是做一个 log,而是抛出异常:

在这里插入图片描述

搭配上 JS 中不存在的类型见擦,当项目规模比较大的情况下,尤其是出现 mixin 套用 mixin 的情况,property 的管理会成为一个比较麻烦的事情。

顺便,只是针对

Object.assign()

的话,它是没有回滚的概念的,也就是说,如果操作中出现一些异常,可能会出现一个部分实现复制的新对象。

总结一下 mixin 的优点和缺点。

优点:

  • 提高代码的复用性
  • 提高代码的灵活性
  • 模块化
  • composition over inheritance这是一个 OOP 常用的概念,组成偏向于继承,这样可以做到比较轻松的修改组成部分的代码,而不用担心影响到继承部分的功能

缺点:

  • 命名冲突假设说多个对象中都存在 id 这个属性,那么 JS 就无法 capture 这个问题
  • 排序很重要假设说多个对象中都存在 id 这个属性,那么最后一个 id 将会重写其他的 id
  • 直接依赖就不太像 spring 一样下面有底层的 DI 管理,很多时候要实现 mixin 还是得手动 cv,这样就会造成直接依赖(必须要知道对象,才能够实现 assign)
  • 提高代码复杂性

reference

  • Object.freeze()
  • SOLID,面向对象设计五大基本原则

本文转载自: https://blog.csdn.net/weixin_42938619/article/details/130926402
版权归原作者 GoldenaArcher 所有, 如有侵权,请联系我们删除。

“JavaScript 高级程序设计 - 第 8 章 理解对象 学习笔记”的评论:

还没有评论