ES6之Object--构造函数与类静态方法

来源:互联网 发布:三毛梦里花落知多少txt 编辑:程序博客网 时间:2024/04/30 14:50

    • 概述
      • 构造函数
      • 重要的属性
      • 类静态方法

概述

Object 类在ES里面使用非常多,Object被当做是一切对象的基类,它是构建JS对象实例的基石,本篇文章主要对ES5以及ES6中Object对象的属性方法以及一些容易犯错的第方做一个总结。

构造函数

构造函数用于创建对象。调用方式为new Object([value])传入相应的值,构建相应类型的对象型。返回的类型由传入的参数的类型决定。还有一种使用方法直接将Object作为普通函数调用,此种方法与构造函数的使用方法一致,返回的都是对应的对象类型。

const a = new Object('hello');console.log(`type is ${typeof a}`);const b = Object('hello');console.log(type is ${typeof b});

有三点需要注意,如果传递的参数类型是基本如string,boolean以及number那么返回的类型是封装类型,即是String Boolean 以及Number类型(这三种类型在typeof返回类型都是object,所以要使用instanceof 判别具体类型)。这三种以外的对象类型比如function,object以及regexp等传入那么返回的是对象本身,返回内容===传入的对象。

const a_1 = new String('test');const a_2 = new Number(12);const a_3 = new Boolean(true);// 完成基本类型的封装console.log(a_1.constructor === String);//trueconsole.log(a_1 instanceof String);//trueconsole.log(a_2 instanceof Number);//trueconsole.log(a_3 instanceof Boolean);//trueconst a_4 = /^\s\w*/gi;const a_5 = (x) => x*x;const a_6 = [1,2,3];// 返回对象本身const a_4obj = new Object(a_4);const a_5obj = new Object(a_5);const a_6obj = new Object(a_6);console.log(a_4obj === a_4);// trueconsole.log(a_5obj === a_5);// trueconsole.log(a_6obj === a_6);// truea_7 = new Object(null);console.log(a_7.constructor);//Object// 使用Object 返回的是对象本身

重要的属性

constructor 属性。返回实例的构造函数。如Array Number Date Object String Boolean Function等。

const c_t1 = 'hello';console.log(c_t1.constructor);//Stringconst c_t2 = [1,2,3];console.log(c_t2.constructor);// Arrayconst c_t3 = new Number(23);console.log(c_t3.constructot);// Numberconsole.log(Array.constructor);// Function 类型函数的构造函数就是Functionconsole.log(Array.prototype.constructor);// Array 类型函数的原型对象的构造函数就是类型本身

有一个点比较重要,JS里面可理解为万物皆对象。function.constructor === Functionfunction.prototype.construction === function(本身)

prototype属性返回类型的原型对象,在function 实例上进行调用。每一个function 都有其原型对象,以function作为构造函数生成的实例是共享一个原型对象,所以JS中的OOP最佳实现都是将函数定义在prototype上面实现所有实例共享同样的函数,并且也支持函数的动态扩展,prototype上面添加函数,所有实例可以访问这个函数。默认的prototype 为Object实例(自定义的函数调用prototype返回的是{}内容为空对象)。

类静态方法

Object.create(prototype,propertydescriptions) 用给定的原型对象与描述对象构建Object实例的函数.propertydescriptions 里面需要对创建的对象的属性进行描述,描述分为四个方面value-初始化值 enumerable-属性是否可以枚举(是否能够访问得到) configurable-是否可配置(决定是否可以删除) writeable 是否可写。构建对象时如果不指定value外的三个参数,那么这个对象的属性是只读的,不可枚举的不可删除的。

const cr_1  = Object.create({},{x:{value:2,writable:true,enumerable:true,configurable:true},y:{value:3}});//这个对象里面X属性是可写可删除可枚举 而y属性是不可写不可删除不可枚举//可写性验证cr_1.x = 10;cr_1.y = 20;console.log(cr_1.x);console.log(cr_1.y);//值不会改变,并且不会报错for(const key in cr_1){    console.log(key);//输出没有y属性 不可枚举}delete cr_1.x;delete cr_1.y;console.log(cr_1.x);// undefined 属性也被删除console.log(cr_1.y);//值不会改变,本身不可删除。

Object.assign(target,...source) 将source中的实例中的所有的可枚举的并且是属于自身的(hasOwnProperty)拷贝到target实例中。相同的名称的属性会被覆盖掉(如果source里面有多个同名的属性,那么最后一个属性将覆盖掉之前所有的属性).字符串或者Symbol定义名称的属性只要满足可枚举并且是hasOwnProperty。

const s = Symbol('t1');const as_t1 = {    name:'hello',    [s]:'vvv',    age:18}Object.assign(as_t1,{    age:20,    [Symbol('t1')]:'vbn'    },{    f:'vbgg',    age:40//age 是相同的属性,所以用后的值进行覆盖});console.log(as_t1);// name age:40 f 不会显示Symbol属性的值console.log(as_t1[s]);//显示vvv 即便是相同标识的Symbol是不同的对象。Object.getOwnPropertySymbols(as_t1);

浅拷贝问题。assign执行的是浅拷贝,如果属性值为以对象,那么实际拷贝的是对象的引用。

const as_ob = {a:'hello',b:{name:'aaa'}};const as_t2 = Object.assign({},as_ob);as_t2.b = 'www';console.log(as_t2);//name 1234console.log(as_ob);// name 1234

原型上的属性以及不可枚举属性都是不能够拷贝进去的

const as_t3ob = Object.create({foo:1},{    bar:{        value:3// 不可枚举不能够拷贝        },    zar:{        value:2,        enumerable:true    }    });//foo 在原型对象上const as_t3 = Object.assign({},as_t3ob);console.log(as_t3);// zar : 2

拷贝过程中的错误会打断当前的拷贝过程,代码停止执行

const as_t4ob = Object.defineProperty({},'foo',{value:2,writable:false});//定义一个不可写的属性fooObject.assign(as_t4ob,{name:'hello',foo:4},{age:5});//第二个对象copy是会出错,代码停止执行console.log(as_t4ob.name);console.log(as_t4ob.foo);console.log(as_t4ob.age);

拷贝对象的属性访问器。对象的属性访问器是对象属性描述(propertydescriptor)实例中的一种
对象的属性描述详解:对象的属性描述是JS对象属性进行定义(Object.defineProperty Object.create())或者更改的时候需要考虑到的对象实例。描述了一个属性的详细行为,它包了三种类型,通用描述configurable以及enumerable,数据描述包含两个属性value,writable访问器描述主要包含get 与set 这两个函数。当访问该属性或者更改该属性的时候调用。
描述对象的默认行为:针对于布尔型对象默认行为为false。也就是说writable,configurable以及enumerable 默认设置为false针对值类型以及函数类型默认的
值为undefined。即是value get set默认为undefinded。最重要一点操作属性的描述时,数据描述与访问器描述不能够混在一起,因为这两者功能一样。value和writable以及get 与set不能够同时存在于一个描述对象。

Object.defineProperty(obj,prop,propertydescriptions)在对象上一个定义新的属性以及其描述或者是更改现有的属性的描述。

Object.defineProperty({},'key',{    value:2,    configurable:false,    writeable:false,    enumerable:false    });

深入理解6种属性的描述
writable:表示属性是否可写,当writable设置为false的时候是不可以写入内容的

const d_t2ob = Object.defineProperty({},'foo',{value:2,writable:false});d_t2ob.foo = 10;//不能够更改,依然是2

enumerable:是否可枚举决定了for in 以及Object.keys()方法能否进行属性访问。false那么以上的方法就不能够获取到

const d_t3ob = Object.defineProperty({},'foo',{value:2,enumerable:false});Object.defineProperty(d_t3ob,'k',{value:10,enumerable:true});for(var key in d_t3ob) console.log(key);kObject.keys(d_t3ob);//['k']

configurable:决定了当前对象是否可以删除,并且决定了该对象其它描述是否可改变。如果为false那么其他描述不可改变。

const d_t4ob = Object.defineProperty({},'key',{value:2,writable:true,configurable:false});d_t4ob.key = 3;console.log(d_t4ob.key);//可以更改Object.defineProperty({},'key',{value:10,writable:false,configurable:false});console.log(d_t4ob.key);// 依然为3Object.defineProperty({},'key',{value:10,writable:false,configurable:false});d_t4ob.key = 30;console.log(d_t4ob.key);// 30 改成false也可以进行写入。

总之当属性描述configurable设置为false时,其他描述都不可改变。
当Define对属性描述修改时,如果传递的新的描述中属性更少,那么原有的属性将会保持不变。

const d_t5ob = Object.defineProperty({},'key',{value:2,configurable:true});console.log(d_t5ob.key);console.log(Object.keys(d_t5ob));// 可遍历Object.defineProperty(d_t5ob,'key',{configurable:true});console.log(d_t5ob.key);console.log(Object.keys(d_t5ob));// 可遍历

get and set 属性访问描述

function d_t6(){    var achive = [];    var temp = null;    Object.defineProperty(this,'tmpr',{            get:function(){                return temp;            },            set:function(value){                temp = value;                achive.push({val:value});            }        });    this.getAcc = function(){        return achive;    };}const d_t6ob = new d_t6();d_t6ob.tmpr = 11;d_t6ob.tmpr = 12;console.log(d_t6ob.getAcc());

Object.defineProperties(obj,props)升级版的defineProperty可以定义多个属性或者更改多个属性

Object.entries(obj)返回自身的可枚举属性的键值对,与for in循环类似但是它不会返回原型链上面的属性键值对。顺序与loop循环顺序一致

const e_t1 = {'a':1,'b':2,'c':3};Object.entries(e_t1).forEach((key, value) => {    console.log(`key is ${key}value is${value}`);    });

不过该方法好像目前用不了。
Object.freeze() 将对象属性的值设为不可更改,并且对象属性的描述符也不可更改,对象本身也不可扩展。

const f_t1 = {a:'hello',b:'fgty'};Object.freeze(f_t1);delete f_t1.a;// do nothingconsole.log(f_t1);Object.defineProperty(f_t1,'foo',{value:2,configurable:true});// 运行报错console.log(f_t1);

freeze中的关键点。如果对象中的属性也是一个对象。那么不可改变的只是这个对象的引用。也就是说对象内部的内容可以改变。

const f_t2 = {a:'hello',b:{}};Object.freeze(f_t2);f_t2.b.key ='world';console.log(f_t2);// 可以添加key属性,浅层次的freeze.

关于进行深层次的freeze。对象本身的值存储的可能是其他对象的引用。要求将对象变为恒定不变的常量。那么要求引用链上的所有对象都必须是freeze的。

function deepFreeze(obj){    const names = obj.getOwnPropertyNames(obj);    names.forEach((name) => {        const val = obj[name];        if(typeof val === 'object'&&val!==null){            deepFreeze(val);        }        });    return Object.freeze(obj);}const f_t3={a:'hello',b:{}};deepFreeze(f_t3);f_t3.b.key='fgh';console.log(f_t3);// 不会添加新的值

Object.getOwnPropertyDescriptor(obj,prop)获取对象属性的描述
属性一定是自身对象所有的不是原型链上的属性。如果属性不存在那么返回undefined。

const g_1 = {b:'hello',get foo(){return 17}};console.log(Object.getOwnPropertyDescriptor(g_1,'foo'));

Object.getOwnPropertyDescriptors(obj)同理返回所有的属性描述符。返回类型为一个对象。如果属性本身没有,那么这个对象也是null的。

//使用这个函数完成浅拷贝Object.create(Object.getPrototypeOf(obj),Object.ggetOwnPropertyDescriptors(obj));//使用这个函数完成子类的定义function Super(){}Super.prototype={};function Sub(){    Super.apply(this);}Sub.prototype = Object.create(Super.prototype,{});

Object.getOwnPropertyNames(obj)获取自身对象上的所有的属性名称返回一个list。list中包含可枚举与不可枚举的所有的自身属性。如果只想获取可枚举的自身属性那么使用Object.keys()方法。

// 使用Array.prototype.filter对数组进行过滤获自身不可枚举属性// keys函数用于获取自身的可枚举的所有的属性。而getOwnPropertyNames(obj)获取自身的所有属性。function get_nonenum(obj){    const enum_arr = Object.keys(obj);    const all_arr = Object.getOwnPropertyNames(obj);    all_arr.filter((item) => {        if(enum_arr.indexOf(item) === -1){            return true;// no_enum        }else{            return false;        }    })}

Object.getOwnPropertySymbols(obj)同names获取的是Symbol类型的属性。
Object.getPrototypeOf(obj)获取属性的原型对象。

const gp_t1 = {};const gp_t1ob = Object.create(gp_t1,{});Object.getPrototypeOf(gp_t1ob) === gp_t1;// true

Object.is(value1,value2) 比较两个value是否一样。is 与 == 以及 === 有不同的地方。
先 == 与 === 的不同
== 在进行比较的时候会先进行类型转换,然后比较是否相等而=== 如果类型不同那么一定不相等。
=== 严格相等的规则如下
只要类型不相同 那么一定不相等
如果对比中出现了至少一个NaN 那么不等,记住NaN === NaN 是false
字符串长度内容一样才相等
数字内容一样相等。注意 n === new Number(n) 为false 因为 typeof new Number(n)是object 而 n === Number(n)是true number(n)返回的是number类型
布尔型 两个都是true或者false 那么返回true
函数以及对象引用。同一个函数同一个对象的引用返回true。
如果两个都是null或者undefined那么返回true

== 比较。类型相同的时候与=== 比较一致
类型不同时有一下规则
null == undefined true
字符串与数字比较先将字符串变为数字然后比较 ‘1’ == 1 true
比较中有true|false 那么会转换为1和0参与比较
对象类型与基本类型。先将对象类型转换为基本类型然后参与比较。

二Object.is() 方法的不同之处在于 第一它不会强制转换类型不同那么就不同返回false
第二 +0 与-0 比较在== 与=== 里面都是true 而使用Object.is(+0,-0)返回false
第三 NaN == NaN 以及 NaN === NaN 返回false 而Object.is(NaN,NaN) 返回ture

Object.isExtensible(obj) 检测对象是否可以拓展.方法Object.preventExtensions以及Object.freeze()和Object.seal()都可以让对象变得不可扩展,返回true.
Object.isFrozen(obj) 对象是否被冻结掉。对象的值不可更改,对象的描述符也不可更改。
Object.isSealed(obj)对象是否被密封掉。
三者的详细的区别后续分析。
Object.keys(obj) 返回对象本身的可枚举的所有属性的array不包含原型链。Object.getOwnPropertyNames(obj) 返回自身所有属性的列表无论是枚举还是不可枚举不包含原型链。for in遍历枚举属性,包括原型链上的属性,原型链要求枚举的属性。

Object.setPrototypeOf(obj,prototype)对比与getPrototypeOf(obj) 设置对象的原型对象.ES6中出现的方法,相比于原有的Object.prototype.proto属性设置来讲更合适。
并且如果第二个参数不是一个对象或者是null那么函数不做任何事情。

Object.setPrototypeOf({},null);// do nothing

Object.values(obj)获取对象的所有属性的值,返回对象的本身可枚举属性值的array,其顺序与for in循环一致。貌似还属于实验性的函数。

0 0
原创粉丝点击