JavaScript基础系列
上期知识点回顾
- var: 使用 var 语句声明的全局变量是全局对象的属性,与其他全局对象属性不同,无法通过 delete 删除
- switch: 语句的 case 判断执行的是 === 比较
- throw: JavaScript 解释器会立即停止当前正在执行的逻辑,跳转到最近的异常处理程序;抛出异常采用的是 Error 类型或其子类型,错误对象有2个属性。
- name: 表示错误类型
- message: 存放传递给构函数的字符串
能够学到的知识点
- 三种创建对象方法
- 三个属性特性
- 三个对象特性
- 三类对象
- 两类属性
对象
JavaScript 对象不仅仅是字符串到值的映射,还可以保持自有的属性(比如:可以从一个称为原型的对象继承属性)。
JavaScript 对象是动态的,可以新增属性或删除属性。
对象常见的用法是创建、设置、查找、删除、检测和枚举它的属性。
每个属性都有的三个属性特性
- 可写(writbale): 表明是否可以设置该属性的值
- 可枚举(enumerable): 表明是否可以通过 for/in 循环返回该属性
- 可配置(configurable): 表明是否可以删除或修改该属性
每个对象都有的三个对象特性
- 对象的原型(prototype): 指向另外一个对象,本对象的属性会继承自它的原型对象
- 对象的类(class): 一个标识对象类型的字符串
- 对象的扩展标记(extensible flag): 是否可以向该对象添加属性
三类对象和两类属性
- 内置对象(native object): 由 ECMAScript 规范定义的对象或类,例如,数组、函数、日期和正则表达式
- 宿主对象(host object): 是由 JavaScript 解释器所嵌入的宿主环境(比如 Web 浏览器)定义的
- 自定义对象(user-defined object): 是由运行中的 JavaScript 代码所创建的对象
- 自有属性(own-property): 直接在对象中定义的属性
- 继承属性(inherited property): 在对象的原型中定义的属性
1.创建对象
对象直接量
var point = { x: 0, y: 0 }
对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。每次计算对象直接量的时候,也都会计算它的每个属性的值。
通过 new 创建对象
new 运算符创建并初始化一个新对象,关键字 new 后跟随一个函数(构造函数)调用。
var obj = new Object({x: 1, y: 2})
console.log(obj); // {x: 1, y: 2}
Object.create()
ECMAScript 5 定义一个名为 Object.create() 方法,它创建一个新对象,其中第一个参数是这个对象的原型,第二个可选参数对对象的属性进行描述。
var obj = Object.create({x: 1, y: 2}, {
x: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
# obj1 继承了属性 x 和 y
var obj1 = Object.create({x: 1, y: 2});
console.log(obj1.__proto__); // {x: 1, y: 2}
console.log(obj1); // {}
console.log(obj1.x); // 1
# obj2 不继承任何属性和方法,包括基础方法 toString()
var obj2 = Object.create(null);
console.log(obj2.toString()); // obj2.toString is not a function
# obj3 等同于 new Object()
var obj3 = Object.create(Object.prototype)
2.属性的查询和设置
可以通过点(.)和方括号([])运算符来获取或更新属性的值。对于点(.)来说,右侧必须是一个属性名称命名的简单标识符;对于方括号([])来说,方括号内可以是一个计算结果为字符串的表达式。
ECMAScript 3 约定点(.)运算符后的标识符不能是保留字(class、for等),而 ECMASCript 5放开了此限制。
属性赋值操作首先检查原型链,以判断是否允许赋值操作。在 JavaScript 中,只有在查询属性时才会体会到继承的存在,而设置属性则和继承无关。
3.删除属性
- delete 只是断开属性和宿主对象的联系,不会去操作属性值
- delete 只能删除自身属性,不能删除继承属性
- delete 表达式删除成功与否没有任何副作用,返回 true
var obj = { x: 1 }
delete obj.x // ture
delete 1 // 无意义
4.检测属性
JavaScript 对象可以看做属性的集合,可以通过 in 运算符、hasOwnProperty() 和 propertyIsEnumerable() 检测属性是否存在某个对象中。
- in: 如果对象的自有属性或继承属性中含有这个属性,返回 true
- hasOwnProperty(): 检测给定的名字是否是对象的自有属性,对于继承属性返回 false
- propertyIsEnumerable(): 检测自有属性具有可枚举性,返回 true
var obj = { x:
"x" in obj; // true,自有属性
"y" in obj; // false
"toString" in obj; // true,继承属性
# hasOwnProperty
obj.hasOwnProperty("x"); // true
obj.hasOwnProperty("y"); // false
obj.hasOwnProperty("toString"); // false
# propertyIsEnumerable
obj.propertyIsEnumerable("x") // true
obj.propertyIsEnumerable("toString") // false
5.枚举属性
for/in 循环可以在循环体重复遍历对象所有可枚举的属性(包括自有属性和继承的属性),对象继承的内置方式不可枚举的,在代码中给对象添加的属性默认是枚举的。
var point = {x: 1, y: 2, z: 3};
point.propertyIsEnumerable('toString'); // fasle
for (p in point) {
if (!point.hasOwnProperty(p)) {
console.log(p); // x, y, z
}
}
- Object.keys() 返回一个数组,数组由对象中可枚举的自有属性的名称组成。
- Object.getOwnPropertyNames() 返回对象的所有属性的名称,而不仅仅是可枚举的属性
var obj = Object.create({}, {
x: {
value: 42,
writable: true,
enumerable: false,
configurable: true
}
});
Object.getOwnPropertyNames(obj); // ['x']
Object.keys(obj); // []
6.属性getter 和 setter
在 ECMAScript 5 中,属性值可以通过 getter 和 setter 方法替代。由 getter 和 setter 定义的属性称做存取器属性。
当程序查询存取器属的值时,JavaScript 调用 getter 方法(无参数),这个方法的返回值就是属性存取表达式的值。
当程序设置一个存取器属性的值时,JavaScript 调用 setter 方法,将赋值表达式右侧的值当做参数传入 setter。
var point = {
// 普通属性
x: 1,
y: 2,
// 存取器属性
get z() {
return this.x + this.y
},
set z(val) {
this.x = this.x + val;
this.y = this.y + val * 2;
},
}
console.log(p); // {x: 1, y: 2}
console.log(point.z) // 3
point.z = 2
console.log(poin) // {x: 3, y: 6}
console.log(Object.keys(point)) //["x", "y", "z"]
7.属性的特性
一个属性包含一个名字和4个特性,数据属性的4个特性分别是它的值(value)、可写性(writable)、可枚举性(enumerable)和可配置性(configurable)。
存取属性不具有值(value)特性和可写性,但仍具有4个特性是读取(get)、写入(set)、可枚举性(enumerable)和可配置性(configurable)。
ECMAScript 5 定义了一个名为 "属性描述符" 的对象,这个对象代表了那4个特性。可以通过 Object.getOwnPropertyDescriptor() 获得。
var point = {
// 普通属性
x: 1,
y: 2,
// 存取器属性
get z() {
return this.x + this.y
},
set z(val) {
this.x = this.x + val;
this.y = this.y + val * 2;
},
}
// {value: 3, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(point, 'x')
// {enumerable: true, configurable: true, get: ƒ, set: ƒ}
Object.getOwnPropertyDescriptor(point, 'z')
要想设置属性的特性,或者想让新建属性具有某种特性,可以调用 Object.defineProperty(),传入要修改的对象、要创建或修改的属性、属性描述符对象。
var obj = {}
Object.defineProperty(obj, 'x', {
value: 1,
writeable: true,
enumerable: false,
configurable: true
});
obj.x // 1
Object.keys(obj) // []
传入 Object.defineProperty() 的属性描述符对象不必包含所有4个特性,对于新创建的属性来说,默认的特性值是 false 或者 undefined。对于修改已有属性来说,默认的特性值没有做任何修改;这个方法无法修改继续属性。
Object.defineProperties() 可以同时修改或创建多个属性特性。
8.对象的三个属性
8.1原型属性(prototype)
对象的原型属性是用来继承属性的,我们经常把 o的原型属性 直接叫做 "o的原型"。
原型属性是在实例对象创建之初就设置好的。
对象原型的设置方法:
- 通过对象直接量创建的对象使用
Object.prototype作为它们的原型
var obj1 = {x: 1, y: 2};
# true
Object.getPrototypeOf(obj1) === Object.prototype
# true
Object.getPrototypeOf(obj1) === Object.getPrototypeOf(new Object)
- 通过
new创建的对象使用构造函数的prototype属性作为它们的原型
var Person = function() {};
var p1 = new Person();
# true
Object.getPrototypeOf(p1) === Person.prototype
# true
Object.getPrototypeOf(p1) == p1.constructor.prototype
# false
p1 === Person.prototype
Object.getPrototypeOf(p1) // {constructor: ƒ}
Person.prototype.constructor.name // Person
p1.constructor.name // Person
p1.__proto__.constructor.name // Person
- 通过
Object.create()创建的对象使用第一个参数(也可以为 null)作为它们的原型
var p2 = Object.create({x: 1, y: 2});
Object.getPrototypeOf(p2) // {x: 1, y: 2}
Object.getPrototypeOf(p2).constructor.name // Object
p2.__proto__.constructor.name // Object
查看对象原型的方法:
-
在 ECMAScript 5 中可以通过
Object.getPrototypeOf()可以查询它的原型。 -
在 ECMAScript 3 中 没有与之等价的函数,可以使用表达式
o.constructor.prototype来检测一个对象的原型。- 通过
new表达式创建的对象,通常继承一个 constructor 属性,这个属性指代创建这个对象的构造函数 - 通过对象直接量或 Object.create() 创建的对象包含一个名为 constructor 的属性,这个属性指代 Object() 构造函数。因此,constructor.prototype 才是对象直接量的真正的原型
- 通过
检查对象原型的方法:
通过 isPrototypeOf() 方法可以检查一个对象是否是另一个对象的原型(或处于原型链中)。
8.2类(class)
对象的类属性是一个字符串,用以表示对象的类型信息。ECMAScript 3 和 ECMAScript 5 都未提供设置这个属性的方法,但可以通过 toString() 方法查询。
- 通过内置构造函数(比如 Array 和 Date) 创建的对象包含类属性,它与构造函数名称相匹配
- 通过对象直接量和 Object.create 创建的对象的类属性是 Object
- 通过自定义构造函数创建的对象的类属性是 Object
function classof(o) {
if (o === null) return 'Null';
if (o === undefined) return 'Undefined';
return Object.prototype.toString.call(o).slice(8, -1)
}
classof(null) // "Null"
classof(undefined) // "Undefined"
classof(1) // "Number"
classof('1') // "String"
classof(false) // "Boolean"
classof([]) // "Array"
classof(new Date) // "Date"
classof(/\d+/) // "RegExp"
var f = function() {}
classof(f) // "Function"
classof(new f()) // "Object"
8.3可扩展性(extensible attribute)
对象的可扩展性用以表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显式可扩展的。宿主对象的可扩展性是由 JavaScript 引擎定义的。
ECMAScript 5 可以通过 Object.isExtensible(obj) 判断对象 obj 是否可扩展的。
ECMAScript 5 可以通过 Object.preventExtensions(obj) 将对象 obj 转换为不可扩展的,此过程是不可逆的。
preventExtensions() 只影响对象本身的可扩展性,如果给一个不可扩展的对象的原型添加属性,这个不可扩展的对象同样会集成这些属性。
var Person = function (name) {
this.name = name;
}
var obj1 = Object.create(new Person('Tom'))
Object.isExtensible(obj1) // true
obj1.age = '12'
obj1.age // '12'
Object.preventExtensions(obj1)
obj1.job = 'fe'
obj1.job // undefined
Person.prototype.city = 'beijing'
obj1.city // "beijing"
Object.seal() 和 Object.isExtensible() 区别是,seal() 方法能够将对象的所有自有属性都设置为不可配置的。
Object.freezz() 将更严格地锁定对象。将对象设置为不可扩展、将其属性设置为不可配置、将自有的所有数据属性设置为只读(如果对象的存取器属性具有 setter 方法,存取器属性不受影响)。
9.对象方法
9.1 toString()
toString() 返回一个表示调用这个方法的对象值的字符串。在需要将对象转换为字符串的时候,JavaScript 会调用这方法,比如使用 "+" 运算符连接字符串时。
9.2 valueOf()
当 JavaScript 需要将对象转换为某种原始值而非字符串的时候才会调用,尤其是转换为数字的时候。