JavaScript学习笔记整理:对象篇

语法


对象两种定义形式

  1. 声明(文字)形式

  2. 构造形式

//声明(文字)形式
var myObj = {
    key: value
    // ...
}

//构造形式
var myObj = new Object();
myObj.key = value;

类型


对象是 JavaScript 的基础。在 JavaScript 中一共有六种主要类型(术语语言类型

  • string

  • number

  • boolean

  • null

  • undefined

  • object

注意,简单基本类型(string,number,boolean,undefined,null)本身并不是对象。

null 有时会被当做对象类型,但这其实只是语言本身的一个 bug,即对 null 执行 typeof null 时会返回"object" 。实际上,null 本身是基本类型。

函数式对象的一个子类型,JavaScript 中的函数是“一等公民”

内置对象


JavaScript 中还有一些对象子类型,通常被称为内置对象。有些内置对象的名字看起来和简单基础类型一样,不过实际上它们的关系更复杂,

  • String

  • Number

  • Boolean

  • Object

  • Function

  • Array

  • Date

  • RegExp

  • Error

这些内置函数可以当做构造函数(由 new 产生的函数调用)来使用,从而可以构造一个对应子类型的新对象。举例来说:

var strPrimitive = "I am string"; //文字形式定义
typeof(strPrimitive);             // "string"
strPrimitive instanceof String;   // false

var strObject = new String("I am string");//构造形式定义
typeof(strObject);                        //"object"
strObject instanceof String;              //true

//检查 sub-type 对象
Object.prototype.toString.call(strObject);//[object String]

Object.prototype.toString...我们可以认为子类型在内部借用了 Object 中的 toString()方法。

由于 javascript 弱类型的编程语言,原始值 "I am string"在必要的时候回自动把字符串字面量转换成一个 String 对象。

思考下面代码:

var strPrimitive = "I am a string";
console.log(strPrimitive.length);   //13
console.log(strPrimitive.charAt(3));//m

使用以上两种方法,我们都可以直接在字符串字面量上访问属性和方法,之所以可以这么做,是因为引擎自动把字面量转换成 String 对象,所以可以访问属性和方法。

null 和 undefined 没有对应的构造形式,他们只有文字形式。相反,Date 只有构造,没有文字形式。

内容


对象的内容是由一些存储在特定命名位置的(任意类型的)值组成的,我们称之为属性。

var myObject = {
    a:2,
    'a_arr':3
}

myObject.a;    //2  属性访问
myObject["a"]; //2  键访问

myObject["a_arr"];  //3

.操作符要求属性命名满足标识符的命名规范,而["..."]语法可以接受任意 UTF-8/Unicode 字符串为属性名

在对象中,属性名永远都是字符串。如果你使用 string(字面量)以外的其他值作为属性名,那它首先会被转换为一个字符串。即使是数字也不例外,虽然在数组下标中使用的的确是数字,但是在对象属性名中数字会被转换成字符串,所以当心不要搞混对象和数字的用法。

var myObject = {}

myObject[true] = "foo";
myObject[3] = "bar";
myObject[myObject] = "baz";

myObject["true"];             //"foo"
myObject["3"];                //"bar"
myObject["[object Object]"];  //"baz"

可计算属性名

ES6 增加了可计算属性名,可以再文字形式中使用[]包裹一个表达式来当做属性名:

var prefix = "foo";

var myObject = {
    [prefix + "bar"]:"hello",
    [prefix + "baz"]:"world",
};

myObject["foobar"]; //hello
myObject["foobaz"]; //world

可计算属性名最常用的场景可能是 ES6的符号(Symbol)

它是一种新的基础数据类型,包含一个不透明且无法预测的值

数组

数组也支持[]访问形式,数组期房的是数值下标,也就是说值存储的位置(索引)是整数。

var arr =[1,"a",2];
arr.length;      //3
arr[0];          //1
arr[1];          //"a"

arr.x = "x";
arr.length;     //3
arr.x;          //"x"

arr["3"] = 3;
arr.length;     //4
arr[3];         //3

数组也是对象,所以虽然每个下标都是整数,你仍然可以给数组添加属性

注意: 如果你试图向数组添加一个属性,当时属性名“看起来”想一个数字,那他会编程一个数值下标(因此会修改数组的内容而不是添加一个属性)

复制对象

javascript 初学者最常见的问题之一就是如何复制一个对象。实际上我们无法选择默认一个复制算法。

举例来说,思考一下这个对象:

function anotherFunction(){/**/}

var anohterObject = {
    c: true
};

var anotherArray = [];

var myObject = {
    a: 2,
    b: anotherObject,//引用,不是副本
    c: anotherArray,//另一个引用
    d: anotherFunction
};

anotherArray.push( anotherObject, myObject);

对象复制时,我们应该判断它是浅复制还是深复制。

对于浅拷贝来说,复制出的新对象中 a 的值会复制旧对象中 b、c、d 引用的对象是一样的。

对于深拷贝来说,除了复制 myObject 以外还会复制 anotherObject 和 anotherArray。这时问题就来了,anotherArray 引用了 anotherObject 和 myObject,所以又需要复制 myObject,这样就会由于循环引用导致死循环。

有一巧妙的复制方法:

var newObj = JSON.parse(JSON.stringify( someObj ));

这种方法需要保证对象是 Json 安全的的,所以只适用于部分情况。

相对于深复制,浅复制就非常易懂并且问题要少得多,所以 ES6定义了 Object.assign(...)方法来实现浅复制。

Object.assign() :第一个参数是目标对象,之后可以跟一个或多个源对象。
它会遍历一个或多个源对象的所有可枚举的自由键并把他们复制到目标对象,最后返回目标对象。

var obj = Object.assign({},myObject);
obj.a;     //2
obj.b === anotherObject;//true
obj.c === anotherArray;//true
obj.d === anotherFunction;//true

属性描述符

ES5之前,JavaScript 语言本身并没有提供可以直接检测属性特性的方法,比如判断属性是否可读。

但是从 ES5开始,所有的属性都具备了属性描述符

思考下面代码:

var myObject = {
    a:2
};

Object.getOwnPropertyDescriptor( myObject,"a" );
// {
//     value:2,
//     writable:true,
//     enumerable:true,
//     configurable:true
// }

可以通过 Object.defineProperty(...) 添加或者修改一个已有属性

Vue 的双向绑定的基础就是基于这个函数,重写 get set 方法,在使用发布-订阅模式来完成数据的动态更新

详情可以看Vue 动态数据绑定(一)和Vue 动态数据绑定三大难点

configurable:false 时:

  1. 它将不能再使用Object.defineProperty(...)进行配置

  2. 但是可以将 writable的状态由 true->false(无法 false->true)

  3. delete 无效

不变性

对象常量

结合 writable:false 和 configurable:false

var myObject ={};

Object.defineProperty( myObject, "NUMBER",{
    value:42,
    writable:false,
    configurable:false
});

禁止扩展

如果你想禁止一个对象添加新属性并且保存已有属性,可以使用 Object.preventExtensions(...)

var myObject ={
    a:2
}
Object.preventExtensions(myObject);

myObject.b =3;
myObject.b; //undefined

在费严格模式下,创建属性 b 会静默失败。在严格模式下,将会抛出 TypeError 错误。

密封

Object.seal(...)会创建一个"密封"的对象,这个访问实际上会在一个现有对象上调用 Object.preventExtensions(...)并把所有现有属性标记为configurable:false。

密封后,不能添加新属性,也不能重新配置或删除任何现有属性(可以修改属性的值)

冻结

Object.freeze(...)会创建一个冻结对象,这个方法实际上会在一个现有对象调用 Object.seal(...)并把所有“数据访问”属性 wirtable:false,这样就无法修改他们的值了


更多内容可以订阅本人微信公众号,一起开启前端小白进阶的世界!

图片描述

你可能感兴趣的:(JavaScript学习笔记整理:对象篇)