语法
// 文字形式
var obj1 = {
name: 'Lee' };
// 构造形式
var obj2 = new Object();
obj2.name = 'Lee';
类型
- 六种主要类型
string
number
boolean
null
undefined
object
- 注意:
- 其中
string
number
boolean
null
undefined
是基本类型,其本身并不是对象 null
有时会被当做一种对象类型,但这其实是语言的一个bug,即对null
执行typeof null
时返回字符串"object"
,但实际上null
是基本类型。错误说法 "JavaScript中万物皆对象"
- 其中
- 对象子类型
函数 Function
(即可调用的对象
,本质上和对象一样,只是可以调用)数组 Array
内置对象
内置对象:
对象的子类型,有的子类型名字看起来和基本类型一样,不过实际上他们的关系更为复杂。
实际上是一些内置函数,可以当作构造函数来使用,用来构造一个对象子类型的心对象。
- 如:
String
Number
Boolean
Object
Function
Array
Date
RegExp
Error
var str = "ProsperLee"; console.log(typeof str); // "string" console.log(str instanceof String); // false console.log(Object.prototype.toString.call(str)); // "[object String]" var strObj = new String("ProsperLee"); console.log(typeof strObj); // "object" console.log(strObj instanceof String); // true console.log(Object.prototype.toString.call(strObj)); // "[object String]" // 语言自动把字面量str转换成了String对象,所以才可以访问的属性和方法 console.log(str.length); // 10 console.log(new String(str).length); // 10 console.log(42.359.toFixed(2)); // "42.36" console.log(new Number(42.359).toFixed(2)); // "42.36"
null
undefined
没有对应的构造形式,只有文字形式。// new null // error null is not a constructor // new undefined // error undefined is not a constructor
Date
只有构造形式,没有文字形式。Object
Array
Function
RegExp
无论使用文字形式还是构造形式,都是对象,不是字面量。Error
一般是在抛出异常时被自动创建,也可以使用new Error(...)
这种构造形式创建。
内容
内容:对象内部存储的属性。
这些属性名称存储在对象容器内,而属性的值其实不是存在对象容器内,属性名称就像指针(引用)一样指向真正值存储的位置。
var obj = {
name: 'Lee', 'abc-def': 123};
// 属性访问(必须满足标识符规范进行使用)
obj.name;
// 键访问(可以接受任意UTF-8/Unicode字符串作为属性名)
obj['name'];
obj['abc-def'];
- 在对象中,属性名永远都是字符串,即使使用除字符串以外的其他值作为属性,也会被转换为字符串。
- 数组下标是数字。
var obj = { 1: 'abc', true: '真', [this]: 'Hello' }; var list = ['abc']; console.log(obj['1'], obj[1]); // abc abc console.log(obj['true'], obj[true]); // 真 真 console.log(obj['this'], obj[this], obj['[object Window]']); // undefined Hello Hello console.log(list[0], list['0']); // abc abc
可计算属性
// Symbol基础数据类型,包含一个不透明且无法预测的值(从技术层面就是一个字符串)
var obj = {
['a' + 'b']: 'Hello',
[Symbol()]: 'Hello'
}
console.log(obj.ab); // Hello
console.log(obj[Symbol()]); // undefined
console.log(obj['Symbol()']); // undefined
属性与方法
叫法:
从技术角度说函数永远不会“属于”一个对象,所以把对象内部引用的函数称为“方法”不妥。
即使在对象的文字形式中声明一个函数表达式,这个函数也不“属于”这个对象,只能算作是对于相同函数对象的多个引用。
var obj = {
foo: function (){
console.log('foo');
}
}
数组
- 数组的下标是数字
- 数组也是对象
- 数组也可以 添加、访问 属性
- 如果添加的属性“看起来像”一个数字,那么会自动处理成下标
var list = [1, 2, 3, 4];
console.log(list[2]); // 3
console.log(list.length); // 4
list.name = 'Lee';
console.log(list.name); // 'Lee'
list['1'] = 'abc';
console.log(list); // [1, 'abc', 3, 4, name: 'Lee']
复制对象
深拷贝
&浅拷贝
参考地址: https://blog.csdn.net/weixin_43526371/article/details/107361037
属性描述符
Object.getOwnPropertyDescriptor
var obj = { name: 'Lee' }; /** * 返回指定对象上一个自有属性对应的属性描述符 * { * value: 'Lee', // 数据值 * writable: true, // 可写 * enumerable: true, // 可枚举 * configurable: true // 可配置 * } */ console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
Object.defineProperty
(value
,writable
,configurable
,enumerable
)var obj = { }; Object.defineProperty(obj, 'name', { value: 'Lee', writable: false, configurable: false, enumerable: false }); console.log(obj); // {name: 'Lee'} // 【writable】 obj.name = 'Tom'; console.log(obj); // {name: 'Lee'} // 【configurable】 /** * Error: Cannot redefine property: name * Object.defineProperty(obj, 'name', { * value: 'Tom', * writable: false, // true false * configurable: false, // true false * enumerable: false // true false * }); */ delete obj.name; console.log(obj); // {name: 'Lee'} // 【enumerable】 for (const key in obj) { // 执行不到遍历,因为只有一个key并且不可枚举 console.log(key); }
不变性
- 定义常量
defineProperty
var obj = { }; Object.defineProperty(obj, 'name', { value: 'Lee', writable: false, configurable: false, });
- 禁止扩展
preventExtensions
var obj = { name: 'Lee' }; Object.preventExtensions(obj); obj.age = 25; // 严格模式下报错 console.log(obj);
- 密封
seal
Object.seal(..)
会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions(..)
并把所有现有属性标记为configurable:false
。- 密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(虽然可以 修改属性的值)。
var obj = { name: 'Lee', list: [1, 2, { age: 25 }], obj: { name: 'Tom' } }; Object.seal(obj); // { value: 'Lee', writable: true, enumerable: true, configurable: false } console.log(Object.getOwnPropertyDescriptor(obj, 'name')); // { value: Array(3), writable: true, enumerable: true, configurable: false } console.log(Object.getOwnPropertyDescriptor(obj, 'list')); // { value: {…}, writable: true, enumerable: true, configurable: false } console.log(Object.getOwnPropertyDescriptor(obj, 'obj')); // { value: 'Tom', writable: true, enumerable: true, configurable: true } console.log(Object.getOwnPropertyDescriptor(obj.obj, 'name')); // { value: 25, writable: true, enumerable: true, configurable: true } console.log(Object.getOwnPropertyDescriptor(obj.list[2], 'age'));
- 冻结
freeze
Object.freeze(..)
会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(..)
并把所有“数据访问”属性标记为writable:false
,这样就无法修改它们的值。
var obj = { name: 'Lee', list: [1, 2, { age: 25 }], obj: { name: 'Tom' } }; Object.freeze(obj); // { value: 'Lee', writable: false, enumerable: true, configurable: false } console.log(Object.getOwnPropertyDescriptor(obj, 'name')); // { value: Array(3), writable: false, enumerable: true, configurable: false } console.log(Object.getOwnPropertyDescriptor(obj, 'list')); // { value: {…}, writable: false, enumerable: true, configurable: false } console.log(Object.getOwnPropertyDescriptor(obj, 'obj')); // { value: 'Tom', writable: true, enumerable: true, configurable: true } console.log(Object.getOwnPropertyDescriptor(obj.obj, 'name')); // { value: 25, writable: true, enumerable: true, configurable: true } console.log(Object.getOwnPropertyDescriptor(obj.list[2], 'age'));
[[Get]]
和[[Put]]
& Getter
和Setter
var obj = {
name: 'Lee' };
var name = 'Lee';
Object.defineProperty(obj, 'name', {
// [[Get]] Getter
get(){
console.log(`[[Get]] Getter ---> ${
name}`);
return name;
},
// [[Put]] Setter
set(value){
console.log(`[[Put]] Setter ---> ${
value}`);
name = value;
}
})
console.log(obj); // { name: (...) }
console.log(obj.name); // [[Get]] Getter ---> Lee Lee
obj.name = 'Tom'; // [[Put]] Setter ---> Tom
存在性
in
&hasOwnProperty
function Person() { this.name = 'Lee'; } Person.prototype.desc = "Hello World"; var person = new Person(); console.log('name' in person); // true console.log('desc' in person); // true console.log('age' in person); // false console.log(person.hasOwnProperty('name')); // true console.log(person.hasOwnProperty('desc')); // false console.log(person.hasOwnProperty('age')); // false
- 判断是否可枚举
var obj = { name: 'Lee', age: 25 }; Object.defineProperty(obj, 'name', { enumerable: false }) Object.defineProperty(obj, 'age', { enumerable: true }) console.log(obj.propertyIsEnumerable('name')); // false console.log(obj.propertyIsEnumerable('age')); // true
Object.keys
包含所有可枚举属性的数组var obj = { name: 'Lee', age: 25 }; Object.defineProperty(obj, 'name', { enumerable: false }) Object.defineProperty(obj, 'age', { enumerable: true }) console.log(Object.keys(obj)); // ['age']
Object.getOwnPropertyNames
包含所有属性的数组var obj = { name: 'Lee', age: 25 }; Object.defineProperty(obj, 'name', { enumerable: false }) Object.defineProperty(obj, 'age', { enumerable: true }) console.log(Object.getOwnPropertyNames(obj)); // ['name', 'age']
遍历
for ... in ...
遍历可枚举属性(遍历的是属性,并非遍历值)- 同数组的
for
循环,实际遍历的是下标
var obj = { name: 'Lee', age: 25 }; Object.defineProperty(obj, 'name', { enumerable: false }) Object.defineProperty(obj, 'age', { enumerable: true }) for (const key in obj) { console.log(key); // age }
- 同数组的
- 遍历数组的辅助迭代器
for ... of ...
数组遍历值/** * // obj is not iterable * var obj = { name: 'Lee', age: 25, desc: '独行者' }; * for (const item of obj) { console.log(item); } */ var list = ['Lee', 'Tom', 'Lucy']; for (const item of list) { console.log(item); // Lee Tom Lucy }
- 原理:
for..of
循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()
方法来遍历所有返回值var list = ['Lee', 'Tom', 'Lucy']; var item = list[Symbol.iterator](); console.log(list); // ['Lee', 'Tom', 'Lucy'] console.log(item); // Array Iterator {} console.log(item.next()); // {value: 'Lee', done: false} console.log(item.next()); // {value: 'Tom', done: false} console.log(item.next()); // {value: 'Lucy', done: false} console.log(item.next()); // {value: undefined, done: true}
- 原理:
for ... of ...
对象遍历值var obj = { name: 'Lee', age: 25, desc: '独行者' }; Object.defineProperty(obj, Symbol.iterator, { enumerable: false, writable: false, configurable: true, value: function () { var o = this; var index = 0; var keys = Object.keys(o); return { next: function () { return { value: o[keys[index++]], done: index > keys.length } } } } }) console.log(obj); // {name: 'Lee', age: 25, desc: '独行者', Symbol(Symbol.iterator): ƒ} for (const item of obj) { console.log(item); // Lee 25 独行者 } var item = obj[Symbol.iterator](); console.log(item); // {next: ƒ} console.log(item.next()); // {value: 'Lee', done: false} console.log(item.next()); // {value: 25, done: false} console.log(item.next()); // {value: '独行者', done: false} console.log(item.next()); // {value: undefined, done: true}
- 迭代器生成‘无限个’随机数的遍历
// 生成无限个随机数 var randoms = { [Symbol.iterator]: function () { return { next: function () { return { value: Math.random() }; } }; } }; // for of 会自动调用执行函数,并返回value for (const iterator of randoms) { console.log(iterator); // 0.4844446296389606 0.7078310386150104 // 当生成的值大于0.5跳出遍历 if(iterator > 0.5) break; } var item = randoms[Symbol.iterator](); console.log(item.next()); // {value: 0.713562025169989}