why-js-day3 对象

语法

  • 声明(文字)形式
let myObj={
    key:value
};
  • 构造形式
let myObj=new Object();
myObj.key=value;

类型

基本类型

  • string
  • number
  • boolean
  • null
  • undefined
  • object

null本身是基本类型,但由于在JavaScript中,对象在底层表示为二进制,前三位为0则为对象,但null全为0,所有typeof会返回object

内置对象(都是对象的子类型)

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error
let str = "i am a string";
console.log(str);
console.log(str instanceof String);
let strObj=new String("I am a string");
console.log(typeof strObj);
console.log(strObj instanceof String);
console.log(Object.prototype.toString.call(strObj));

string本身并不是对象,但执行操作时(获取长度)会自动转换为对象String

let str="i am a string";
console.log(str.length);
console.log(str.charAt(3));

内容

对象的内容即属性

属性的访问方式

  • 属性访问,  ( . ) 操作符要求属性名满足标识符的命名规范
  • 键访问, [''] 可以接受任何Unicode字符串作为属性名
let myobj={
    a:2
};
console.log(myobj.a);
console.log(myobj['a']);

由于['']接受字符串,可动态构造这个字符串

let myobj={
    a:2
};
let idx,
    something=true;
if (something){
    idx='a';
} 
console.log(myobj[idx]);

在对象中,属性名永远是字符串,即使是其他值也会转换为字符串(数组中确实是数字)

let myobj={};
myobj[true]='foo';
myobj[3]='bar';
myobj[myobj]='baz';
console.log(myobj['true'],myobj['3'],myobj['[object Object]']);

可计算属性名,在文字形式中通过[]包裹一个表达式来当做属性名

let prefix='foo';
let name='javascript';
let myobj={};
myobj[prefix+name]='true';
console.log(myobj['foojavascript']);
let myobj2={
    [prefix+'bar']:"hello",
    [prefix+'baz']:"world"
};
console.log(myobj2['foobar'],myobj2['foobaz']);

属性与方法

在JavaScript函数和方法是可以互换的,函数永远不会属于一个对象,只有对函数的引用

function foo() {
    console.log("foo");
}
let someFoo=foo;
let myObj={
    someFoo:foo
};
console.log(foo,someFoo,myObj.someFoo);

即使在对象内部声明一个函数,这个函数也不会属于这个对象,只是一个引用

let myobj={
    foo:function foo() {
        console.log('foo');
    }
};
let someFoo=myobj.foo;
console.log(someFoo,myobj.foo);

数组

数组也支持[]访问,不过期望的是整数,数组也是对象,可以添加属性

let myarray=['foo',42,'bar'];
console.log(myarray.length,myarray[0],myarray[2]);
myarray.baz="baz";
console.log(myarray.length,myarray.baz);

如果向数组添加一个属性,但属性看起来是数字,就是变为数值小标,并改变数组

let myarray=['foo',42,'bar'];
myarray['3']='baz';
console.log(myarray.length,myarray[3]);

复制对象

function anotherfun() {}
let anotherobj={
    c:true
};
let anotherarr=[];
let myobj={
    a:2,
    b:anotherobj,
    c:anotherarr,
    d:anotherfun
};
anotherarr.push(anotherobj,myobj);

属性描述符

  • value
  • writable
  • enumerable
  • configurable
let myobj={
    a:2
};
console.log(Object.getOwnPropertyDescriptor(myobj,'a'));

创建普通属性时,属性描述符会使用默认值,也可通过Object.defineProperty()添加或修改属性

let myobj={};
Object.defineProperty(myobj,'a',{
    value:2,
    writable:true,
    configurable:true,
    enumerable:true
});
console.log(myobj.a);
  • writable,决定是否可以修改属性值(静默失败,严格模式下,会报错)
let myobj={};
Object.defineProperty(myobj,'a',{
    value:2,
    writable:false,
    configurable:true,
    enumerable:true
});
myobj.a=3;
console.log(myobj.a);
  • configurable,只要属性是可配置的就可以使用defineProperty()方法来修改属性描述符,把configurable设为false是单向操作无法撤销,但writable属性可以由true改为false,但无法false-->true
let myobj={
    a:2
};
myobj.a=3;
console.log(myobj.a);
Object.defineProperty(myobj,'a',{
    value:4,
    writable:true,
    configurable:false,
    enumerable:true
});
console.log(myobj.a);
myobj.a=5;
console.log(myobj.a);
Object.defineProperty(myobj,'a',{
    value:6,
    writable:true,
    configurable:true,
    enumerable:true
});

同时,还会禁止删除这个属性(静默失败,严格模式报错)

let myobj={
    a:2
};
console.log(myobj.a);
delete myobj.a;
console.log(myobj.a);
Object.defineProperty(myobj,'a',{
    value:2,
    writable:true,
    configurable:false,
    enumerable:true
});
console.log(myobj.a);
delete myobj.a;
console.log(myobj.a);
  • enumerable,控制属性是否出现在对象的枚举属性中(如for..in)

不变性

对象或属性不可改变,所有方法都是浅不变,只会影响目标对象和直接属性,不会改变引用对象

  • 对象常量,结合writable:false和configurable:false创建一个常量属性(不可修改,重定义,删除)
let myobj={};
Object.defineProperty(myobj,'a',{
    value:42,
    writable:false,
    configurable:false
});
  • 禁止扩展,禁止对象添加新属性和保留已有属性(严格模式下会报错)
let myobj={
    a:2
};
Object.preventExtensions(myobj);
myobj.b=3;
console.log(myobj.b);
  • 密封,Object.seal(),对现有对象调用Object.preventExtensions()并把所有属性标记为configurable:false
  • 冻结,Object.freeze(),对现有对象调用Object.seal(),并将所有属性标记为writable:false

[[Get]]

属性访问实现时的细节

let myobj={
    a:2
};
console.log(myobj.a);

对象默认的内置[[Get]]操作会首先在对象中查找是否有相同名称的属性,找打则返回,反之,则遍历可能存在的[[Prototype]]链,如果仍没找到则返回undefined

let myobj={
    a:2
};
console.log(myobj.b);

[[Put]]

属性存在时的行为

  1. 属性是否是访问描述符?如果是并存在setter就调用setter
  2. 属性的数据描述符中writable是否为false,是就静默失败,严格模式抛出TypeError异常
  3. 如果都不是则将该值设为属性的值

Getter和Setter

getter是一个隐藏函数会在获取属性时调用,setter会在设置值时调用,当给属性定义getter和setter时,这个属性会被定义为访问描述符,JavaScript会忽略他们的value和writable,取而关心set和get(还有configurable和enumerable)特性

let myobj={
    get a(){
        return 2;
    }
};
Object.defineProperty(myobj,'b',{
    get:function () {
        return this.a*2;
    },
    enumerable:true
});
console.log(myobj.a,myobj.b);

定义setter,会覆盖单个属性默认的[[Put]]操作

let myobj={
    get a(){
        return this._a_;
    },
    set a(val){
        this._a_=val*2;
    }
};
myobj.a=2;
console.log(myobj.a,myobj._a_);

存在性

let myobj={
    a:undefined
};
console.log(myobj.a,myobj.b);

这种情况下无法确定属性是否存在

通过in操作符检查属性是否在对象及其[[Prototype]]原型链中,hasOwnPrototy(...)只检查属性是否在对象中,对于一些对象没有Object.prototype的委托,即没有原型(Object.create(null)创建的对象),不能调用hasOwnPrototy(...).此时可显示绑定,Object.prototype.hasOwnPrototy(obj,'a')

let myobj={
    a:2
};
console.log('a'in myobj,'b' in myobj,myobj.hasOwnProperty('a'),myobj.hasOwnProperty('b'));

in操作符实际检查的是某个属性名是否存在,尤其是数组

list = [1,4,3];
console.log(4 in list,2 in list);

枚举

属性存在且可访问,但不会在for..in循环中,尽管能用in操作符来判断是否存在

let myobj={};
Object.defineProperty(myobj,'a',{
    enumerable:true,
    value:2
});
Object.defineProperty(myobj,'b',{
    enumerable:false,
    value:3
});
console.log(myobj.b);
console.log('b' in myobj,myobj.hasOwnProperty('b'));
for (let k in myobj){
    console.log(k,myobj[k]);
} 

另一种区分属性是否可枚举

  • propertyIsEnumerable(...)会检查属性名是否直接(不是原型链中)存在对象中并满足enumerable:true
  • Object.keys(...)返回一个数组,包含所有可枚举属性
  • Object.getOwnPropertyNames(...)返回一个数组包含所有属性,无论是否可枚举
  • in和hasOwnProperty区别在于是否查找[[Prototype]]链,Object.keys(...),Object.getOwnPropertyNames(...)都直会直接查找对象包含的属性
let myobj={};
Object.defineProperty(myobj,'a',{
    enumerable:true,
    value:2
});
Object.defineProperty(myobj,'b',{
    enumerable:false,
    value:3
});
console.log(myobj.propertyIsEnumerable('a'),myobj.propertyIsEnumerable('b'));
console.log(Object.keys(myobj),Object.getOwnPropertyNames(myobj));

遍历

for...in可以遍历对象可枚举属性列表(包括[[Prototype]]链)

forEach(...),every(...),some(...),辅助迭代器可以接受一个回调函数并把它引用到数组的每个元素上

forEach(...)会遍历数组的所有值并忽略回调函数的返回值

every(...)会一直运行到回调函数返回false

some(...)会一直运行到回调函数返回true

遍历对象属性的顺序是不确定,一定不要相信观察到的顺序,这是不可靠的

遍历值for...of,对象需要实现迭代器

let myarray=[1,2,3];
for (let v of myarray){
    console.log(v);
}

for...of会先对被访问对象请求一个迭代器对象,然后调用迭代器对象的next()方法遍历所有值,

Symbol.iterator用来获取对象的@@iterator内部属性,引用类似iterator的特殊属性时要使用符号名,而不是符号包含的值,@@iterator本身并不是迭代器对象,而是一个返回迭代器对象的函数,

next(...)返回形式为{value:..,done:...},value是当前遍历的值,done是一个bool表示是否还有可遍历值

let myarray=[1,2,3];
let it=myarray[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

给对象定义@@iterator

let myobj={
    a:2,
    b:3
};
Object.defineProperty(myobj,Symbol.iterator,{
    enumerable:false,
    writable:false,
    configurable:true,
    value:function () {
        let o=this;
        let idx=0;
        let ks=Object.keys(o);
        return {
            next:function () {
                return {
                    value:o[ks[idx++]],
                    done:(idx>ks.length)
                };
            }
        };
    }
});
let it=myobj[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
for (let v of myobj){
    console.log(v);
}

定义无限迭代器

let randoms={
    [Symbol.iterator]:function () {
        return {
            next:function () {
                return {value:Math.random()};
            }
        };
    }
};
let randoms_pool=[];
for (let n of randoms){
    randoms_pool.push(n);
    if (randoms_pool.length ===100)break;
}

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值