对象属性遍历
练习
function Car(brand, color, displacement) {
this.brand = brand;
this.color = color;
this.displacement = displacement;
this.getInfo = function () {
return `排量为${this.displacement}的${this.color}${this.brand}`;
};
}
function Person(options) {
Car.apply(this, [options.brand, options.color, options.displacement]);
this.name = options.name;
this.age = options.age;
this.introduce = function () {
console.log(`年龄${this.age}岁,${this.name}买了一辆${this.getInfo()}`);
};
}
const person = new Person({
brand: '奔驰',
color: '红色',
displacement: '3.0',
name: '张三',
age: '25',
});
person.introduce();
console.log(person);
链式操作
当方法返回当前对象 (this
) 时,可以实现链式调用其他方法。在 JavaScript 中,对象作为引用值传递,因此每个方法返回对象的引用,使得类支持链式调用。
const schedule = {
wakeUp() {
console.log('起床');
return this;
},
morning() {
console.log('去购物');
return this;
},
noon() {
console.log('午休');
return this;
},
afternoon() {
console.log('学习');
return this;
},
evening() {
console.log('散步');
return this;
},
night() {
console.log('睡觉');
return this;
},
};
schedule.afternoon().morning().noon().night().evening();
拼接数字
JavaScript 引擎最初支持 obj['name']
语法,后来点语法出现后,内部会隐式转换为 obj['name']
。
const myLanguages = {
No1: 'HTML',
No2: 'CSS',
No3: 'JavaScript',
getStudyingLanguage(num) {
console.log(this[`No${num}`]);
},
};
myLanguages.getStudyingLanguage(1);
const obj = {
name: '123',
};
console.log(obj['name']);
对象枚举
枚举是具有共同特性的数据集合,遍历是按顺序获取集合中每一项的过程。在 JavaScript 中,遍历和枚举紧密相关。
遍历数组
const array = [1, 2, 3, 4, 5];
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}
const array = [1, 2, 3, 4, 5];
for (const index in array) {
console.log(array[index]);
}
访问内部属性
const car = {
brand: '奔驰',
color: '红色',
displacement: '3.0',
};
for (const key in car) {
// 遍历对象的内部属性
console.log(key);
// 使用 car[key] 访问属性值
console.log(`${key}: ${car[key]}`);
}
打印原型链上的所有属性
function Car() {
this.brand = '品牌';
this.color = '红色';
this.displacement = '3.0';
}
Object.prototype.owner = '车主';
Car.prototype.language = 'JavaScript';
Car.prototype.width = 2.5;
const car = new Car();
for (const key in car) {
console.log(`${key}: ${car[key]}`);
}
hasOwnProperty
hasOwnProperty
方法用于判断对象自身是否具有指定属性,不会检查原型链上的属性。
const car = {
brand: '奔驰',
color: '红色',
displacement: '3.0',
};
Car.prototype.language = 'JavaScript';
Car.prototype.width = 2.5;
for (const key in car) {
if (car.hasOwnProperty(key)) {
console.log(`${key}: ${car[key]}`);
}
}
in 判断属性
in
运算符用于判断属性是否存在于对象中,包括原型链上的属性。
const car = {
brand: '奔驰',
color: '红色',
};
console.log('color' in car); // true
function Car() {
this.brand = '奔驰';
this.color = '红色';
}
Car.prototype.displacement = '3.0';
const myCar = new Car();
console.log('displacement' in myCar); // true
instanceof
instanceof
用于判断对象是否由特定构造函数创建。
function Car() {}
const car = new Car();
function Person() {}
const person = new Person();
console.log(car instanceof Car); // true
console.log(person instanceof Car); // false
console.log(person instanceof Object); // true
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
console.log({} instanceof Object); // true
使用 toString() 检测对象类型
const array = [];
console.log(array instanceof Array); // true
const toString = Object.prototype.toString;
const arrayTag = '[object Array]';
if (toString.call(array) === arrayTag) {
console.log('是数组');
} else {
console.log('不是数组');
}
this
this
的指向根据函数的调用方式不同而不同。
- 普通函数:全局的
this
指向window
。 - 预编译函数:
this
指向window
。 call
/apply
:可以改变this
的指向。- 构造函数:
this
指向实例化后的对象。 this
在执行期决定指向,不稳定。- 箭头函数:
this
指向父环境的this
,较为稳定。
普通函数内部的 this
function test() {
this.d = 3;
var a = 1;
function inner() {}
}
test();
// 全局变量 d 被赋值为 3
console.log(d); // 3
console.log(this.d); // 3
console.log(window.d); // 3
构造函数的 this
function Test() {
this.name = '张三';
}
const instance = new Test();
console.log(instance.name); // 张三
call/apply
function Person(name, age) {
this.name = name;
this.age = age;
}
function Programmer(name, age) {
Person.apply(this, [name, age]);
this.profession = '程序员';
}
const programmer = new Programmer('李四', 28);
console.log(programmer);
callee/caller
callee
arguments.callee
返回正在执行的函数对象。适用于递归调用。
function test(a, b, c) {
console.log(arguments.callee.length); // 3
console.log(test.length); // 3
console.log(arguments.length); // 实际传入参数个数
console.log(arguments.callee);
}
test(1, 2);
递归累加 n 位数
function sum(n) {
if (n <= 1) {
return 1;
}
return n + arguments.callee(n - 1);
}
const result = sum(10);
console.log(result); // 55
caller
caller
返回调用当前函数的函数引用。
function test1() {
test2();
}
function test2() {
console.log(test2.caller); // 输出 test1 函数
}
test1();
严格模式下的 caller
在严格模式下,caller
和 callee
不可用。
'use strict';
function test1() {
test2();
}
test1();
function test2() {
console.log(test2.caller); // TypeError
}
练习题
arguments
function foo() {
bar.apply(null, arguments);
}
function bar() {
console.log(arguments);
}
foo(1, 2, 3, 4, 5);
// 输出 Arguments(5) [1, 2, 3, 4, 5]
typeof 可能返回的值
typeof
运算符可能返回以下值:
- "object"(包括
null
) - "boolean"
- "number"
- "string"
- "undefined"
- "function"
const f =
(function () {
return '1';
},
function () {
return 2;
});
console.log(typeof f); // "function"
const f = (function () {
return '1';
},
function () {
return 2;
})();
console.log(typeof f); // "number"
形参和实参的映射关系
形参与实参之间存在映射关系,修改形参会影响 arguments
对象。
function example(x, y, a) {
a = 10;
console.log(arguments[2]);
}
example(1, 2, 3); // 输出 10
比较
console.log(undefined == null); // true
console.log(undefined === null); // false
console.log(isNaN('100')); // false
console.log(isNaN('abc')); // true
console.log(parseInt('1') == 1); // true
console.log(parseInt('1abc') == 1); // true
isNaN 的实现原理
function isNaNCustom(value) {
const result = Number(value);
return result !== result;
}
console.log(isNaNCustom('abc')); // true
console.log(NaN === NaN); // false
空对象比较
console.log({} == {}); // false
const obj = {};
const obj1 = obj;
console.log(obj1 === obj); // true
全局和局部作用域
var a = '1';
function test() {
var a = '2';
this.a = 3;
console.log(a);
}
test(); // 输出 '2'
new test(); // 输出 '2'
console.log(a); // 输出 '3'
var b = '5';
function testScope() {
var b = '0';
console.log(b); // 0
console.log(this.b); // 5
}