JavaScript基础提升【三】
目录
JavaScript构造函数
向JavaScript构造函数添加方法
new.target
JavaScript原型继承
在ES5中实现原型继承的标准方法
JavaScript对象属性
对象属性类型
数据属性
访问器属性
定义多个属性
JavaScript对象属性描述符
JavaScript可枚举属性
JavaScript对象解构
设置默认值
解构空对象
嵌套对象解构
JavaScript可选链运算符
ES6中的对象字面量语法扩展
对象属性初始化简写
计算属性
其它
JavaScript构造函数
构造函数是一个普通的函数,遵循以下约定:
- 构造函数的名称以大写字母开头,例如:Person、Document
- 构造函数只能使用“new”运算符调用
以下示例定义了一个名为“Person”的构造函数
function Person(firstName, lastName) {this.firstName = firstName;this.lastName = lastName;
}let person = new Person("John", "Doe");
“new”运算符执行以下操作:
- 创建一个新的空对象,并将其分配给this变量
- 将参数John、Doe分配给this变量的firstName和lastName
- 返回this
它等效于:
function Person(firstName, lastName) {const thisObject = {};thisObject.firstName = firstName;thisObject.lastName = lastName;return thisObject;
}let person = Person("John", "Doe");
向JavaScript构造函数添加方法
对象可能包含用于操作其数据的方法,同样使用this关键字向构造函数添加方法:
function Person(firstName, lastName) {this.firstName = firstName;this.lastName = lastName;this.getFullName = function() {return this.firstName + " " + this.lastName;}
}let person = new Person("John", "Doe");console.log(person.getFullName()); // John Doe
但是这样有一个问题,如果你创建了非常多的Person实例,每个实例都有一个getFullName这样非常浪费内存空间,所以我们可以使用“原型”来解决问题,一个简单的原型如下所示:
function Person(firstName, lastName) {this.firstName = firstName;this.lastName = lastName;
}// 使用prototype
Person.prototype.getFullName = function() {return this.firstName + " " + this.lastName;
}
// 或使用__proto__
let person = new Person("John", "Doe");
person.__proto__.getFullName = function() {return this.firstName + " " + this.lastName;
}
// 或使用getPrototypeOf
Object.getPrototypeOf(person).getFullName = function() {return this.firstName + " " + this.lastName;
}console.log(person.getFullName()); // John Doe
new.target
ES6标准新增new.target属性,如果构造函数不是被new关键字调用,而是当做普通函数调用,那么它返回undefined,同时函数也返回undefined。
function Person(firstName, lastName) {console.log(new.target); // undefinedthis.firstName = firstName;this.lastName = lastName;this.getFullName = function() {return this.firstName + ' ' + this.lastName;}
}const person = Person('John', 'Doe');
console.log(person); // undefined
利用这个特性,我们可以使语法更加灵活:
function Person(firstName, lastName) {if (!new.target) {return new Person(firstName, lastName);}this.firstName = firstName;this.lastName = lastName;this.getFullName = function() {return this.firstName + ' ' + this.lastName;}
}const person = Person('John', 'Doe');
console.log(person.getFullName()); // John Doe
JavaScript原型继承
假设定义了一个person对象:
const person = {name: "John Doe",greet: function() {return `Hello, my name is ${this.name}`;}
}
person对象有一个属性和方法
- name:一个属性,存储名字
- greet:一个方法,打印招呼
默认情况下,JavaScript引擎会为person提供一个内置的Object()函数和一个匿名对象,可以使用Object.prototype来引用:

- 圆圈代表函数,正方形代表对象
person对象与Object()函数prototype对象之间存在链接
(相当于person对象是Object()函数的一个示例)

此时再定义一个teacher对象,它有一个teach()方法:
const teacher = {teach: function(subject) {return "I can teach " + subject;}
}
它同样有Object.prototype原型:

此时person和teacher是兄弟的关系,彼此无法使用对方的方法,但是可以将teacher的原型设置为person:
const person = {name: "John Doe",greet: function() {return `Hello, my name is ${this.name}`;}
}
const teacher = {teach: function(subject) {return "I can teach " + subject;}
}
teacher.__proto__ = person;console.log(teacher.greet()); // Hello, my name is John Doe

在ES5中实现原型继承的标准方法
ES5提供了一种使用Object.create()方法来处理原型继承的标准方法
Object.create()方法创建一个新对象,并使用现有对象作为新对象的原型:
Object.create(proto, [propertiesObject])
Object.create()方法接收两个参数:
- proto:用作新对象原型的对象
- propertiesObject(可选):定义了新对象的附加属性
const person = {name: "John Doe",greet: function() {return `Hello, my name is ${this.name}`;}
}const teacher = Object.create(person, {teach: function () {return `${this.name} is now teaching.`;}
})console.log(teacher.greet()); // "Hello, my name is John Doe"
JavaScript对象属性
对象属性类型
JavaScript通过用两对方括号包围的内部属性来指定对象属性的特征(属性也有属性,有点绕)
对象有两种类型的属性:“数据属性”和“访问器属性”
数据属性
数据属性包含一个用于数据值的单一位置。数据属性具有四个属性:
- [[ Configurable ]]:确定是否可以通过delete运算符重新定义或删除属性
- [[ Enumerable ]]:指定属性是否可以在“for...in”循环中返回
- [[ Writable ]]:指定属性的值是否可以更改
- [[ Value ]]:包含属性的实际值
默认情况下,[[ Configurable ]]、[[ Enumerable ]]、[[ Writable ]]属性对于直接在对象上定义的所有属性都设置为true,[[ Value ]]属性的默认值为undefined
如果使用Object.defineProperty()方法给对象添加属性,那么[[ Configurable ]]、[[ Enumerable ]]、[[ Writable ]]的默认值都是false
Object.defineProperty()方法接收三个参数:
- 一个对象
- 对象的属性名称
- 一个属性描述符对象,该对象具有四个属性:configurable、enumerable、writable、value
const test = {};Object.defineProperty(test, 'value', {configurable: false,enumerable: true,writable: false,value: 100
});console.log(test.value);
test.value = 200; // 错误,value属性是只读的
delete test.value; // 错误,value属性是不可配置的
如果一个属性的configurable是false,那么他将不能被defineProperty配置:
Object.defineProperty(test, 'value', {configurable: false,enumerable: true,writable: false,value: 100
});
Object.defineProperty(test, 'value', {configurable: true,
}); // 错误,value属性是不可配置的
访问器属性
访问器属性有四个属性:
- [[ Configurable ]]:控制属性是否可以配置
- [[ Enumerable ]]:控制属性是否可以在for...in中被遍历
- [[ Get ]]:尝试访问属性时,执行该函数
- [[ Set ]]:尝试修改属性时,执行该函数
定义访问器属性,必须使用Object.defineProperty()方法:
const test = {firstName: 'John',lastName: 'Doe'
};Object.defineProperty(test, 'value', {get: function() {return this.firstName + ' ' + this.lastName;},set: function(value) {if (typeof value !== 'string') {throw new Error('Invalid value');}else {this.firstName = value.split(' ')[0];this.lastName = value.split(' ')[1];}}
})console.log(test.value); // John Doe
test.value = 'Jane Doe';
console.log(test.value); // Jane Doe
test.value = 123;
console.log(test.value); // Invalid value
test.value = new String(123);
console.log(test.value); // Invalid value
定义多个属性
可以使用Object.defineProperties()方法在单个语句中定义多个属性:
const product = {};Object.defineProperties(product, {name: {value: 'Apple'},price: {writable: true,value: 100},getInfo: {get: function () {return `${this.name} ${this.price}`;}},setPrice: {set: function (newPrice) {this.price = newPrice;}}
})console.log(product.getInfo);
product.setPrice = 200;
在这里定义两个数据属性:“name”、“price”,两个访问器属性:“getInfo”、“setPrice”
JavaScript对象属性描述符
Object.getOwnPropertyDescriptor()方法允许您获取属性的描述符对象,描述符对象包含四个属性:
- [[ Configurable ]]:确定是否可以通过delete运算符重新定义或删除属性
- [[ Enumerable ]]:指定属性是否可以在“for...in”循环中返回
- [[ Writable ]]:指定属性的值是否可以更改
- [[ Value ]]:包含属性的实际值
Object.getOwnPropertyDescriptor()接收两个参数:“对象名”、“属性名”
const person = {firstName: 'John',lastName: 'Doe'
};const descriptor = Object.getOwnPropertyDescriptor(person, 'firstName');
console.log(descriptor); // { value: 'John', writable: true, enumerable: true, configurable: true }
JavaScript可枚举属性
可枚举属性可以使用for...in循环或Objects.keys()方法进行迭代
可枚举属性的enumerable为true时才可以迭代
ES6提供了一个方法:propertyIsEnumerable(),用于确定属性是否可枚举
如果属性可枚举返回true,否则返回false
const person = {firstName:"John",lastName:"Doe",
};person.age = 20;Object.defineProperty(person, 'ssn', {enumerable: false,value: '123-45-6789'
})console.log(person.propertyIsEnumerable('ssn')); // false
console.log(person.propertyIsEnumerable('firstName')); // true
console.log(person.propertyIsEnumerable('lastName')); // true
console.log(person.propertyIsEnumerable('age')); // true
JavaScript对象解构
对象解构是将属性从另一个对象分配给变量的便捷语法
const person = {firstName:"John",lastName:"Doe",
};const { firstName: fName, lastName: lName } = person;
冒号(:)之前的标识符是对象的属性,冒号之后的标识符是对象
如果变量与对象的属性同名,可以用下面的方式使代码更简洁:
const person = {firstName:"John",lastName:"Doe",
};const { firstName, lastName} = person;
console.log(firstName); // John
console.log(lastName); // Doe
但是使用对象解构将不存在的属性分配给变量时,该变量将被设置为“undefined”
let { firstName, lastName, middleName } = person;
console.log(middleName); // undefined
设置默认值
当对象的属性不存在时,可以将默认值分配给变量:
let person = {firstName: 'John',lastName: 'Doe',currentAge: 28
};let { firstName, lastName, middleName = '', currentAge: age = 18 } = person;console.log(middleName); // ''
console.log(age); // 28
解构空对象
有些时候,解构的函数返回值可能返回一个空值,这个时候直接解构是报错的:
function getPerson() {return null;
}
let { firstName, lastName } = getPerson();console.log(firstName, lastName); // undefined undefined
为了避免这种情况,可以使用OR运算符(||)将null对象回退到一个空对象
let { firstName, lastName } = getPerson() || {};
此时firstName和lastName将为undefined
嵌套对象解构
嵌套对象解构只需要在对应位置放置对应变量即可:
let employee = {id: 1001,name: {firstName: 'John',lastName: 'Doe'}
};
let {name: {firstName,lastName}
} = employee;console.log(firstName); // John
console.log(lastName); // Doe
JavaScript可选链运算符
可选链运算符(?.)就像一个访问对象系列中嵌套属性的快捷方式。这样就无需检查链中每个步骤是否为空(null或undefined),而是可以使用链运算符?.直接访问所需的属性
如果链的任何部分为空,可选链运算符(?.)将立即停止并返回undefined运算符为结果。它可以避免您为链中每个步骤编写额外的检查。
function getUser(id) {if(id <= 0) {return null;}return {id: id,username: 'admin',profile: {avatar: '/avatar.png',language: 'English'}}
}// 之前的做法
let user = getUser(2);
let profile = user && user.profile;
// 可选链运算符
let user = getUser(2);
let profile = user ?. profile;
ES6中的对象字面量语法扩展
对象属性初始化简写
在ES6之前,对象字面量是:“键:值”对的集合:
function createMachine(name, status) {return {name: name,status: status};
}
name、status在对象中出现了两次,这样写起来很冗余,在ES6之后可以:
function createMachine(name, status) {return {name,status};
}
计算属性
一个对象的属性名如果是一个字符串,就必须使用[]方括号调用:
let name = 'machine name';
let machine = {[name]: 'server','machine hours': 10000
};console.log(machine[name]); // server
console.log(machine['machine hours']); // 10000
其它
更多JavaScript学习可以参考我的专栏:JavaScript_是洋洋a的博客-CSDN博客
