目录
(4)setPrototypeOf、getPrototypeOf
1、对象拓展
ES6 为 JavaScript 的Object对象带来了一系列实用的新方法,这些方法极大地提升了对象操作的便捷性与灵活性,帮助开发者更高效地编写代码。
(1)简写对象
在 ES6 中,允许在对象字面量的大括号内直接写入变量和函数,作为对象的属性和方法。这种写法能够有效减少冗余代码,显著提升代码的可读性与简洁性。
// ES6允许在大括号里直接写入变量和函数,作为对象的属性和方法,书写更简洁
let name = 'bdqn';
let change = function () {
console.log('我们可以改变你!!');
};
const school = {
name, // 当属性名和变量名一致时,可直接简写
change, // 外部定义的函数可直接作为对象属性
fun: function () {
console.log("我是复杂写法");
},
improve() {
// 直接在对象里定义函数
console.log("我是简写");
}
};
console.log(school);
注意:在后续开发中,建议优先采用这种简洁的对象简写形式。它不仅能提高代码编写效率,还能增强代码的可维护性。例如在构建配置对象时,使用简写形式会让代码更加清晰易读 。
(2)Object.is
Object.is()方法用于判断两个值是否严格相等,其行为与===运算符基本一致,但在处理+0与NaN时存在差异。
console.log(Object.is(120, 120)); // true,与 === 行为一致
console.log(Object.is(NaN, NaN)); // true,NaN 与自身严格相等
console.log(NaN === NaN); // false,=== 认为 NaN 与自身不相等
在实际应用中,当需要精确判断两个值是否完全相等,尤其是涉及NaN等特殊值时,Object.is()方法能提供更准确的结果。
(3)Object.assign
Object.assign()方法用于合并对象,它会将源对象的所有可枚举属性复制到目标对象中。如果存在相同属性,后面源对象的属性会覆盖目标对象的属性。
Object.assign(target, object1, object2);
- target:合并后存放属性的目标对象。
- object1:第一个源对象。
- object2:第二个源对象,可根据需要添加更多源对象。
const obj1 = {
name: "tom",
age: 18
};
const obj2 = {
name: "bob",
age: 28,
sex: "男"
};
console.log(Object.assign(obj1, obj2));
// {name: 'bob', age: 28, sex: '男'},obj2 的属性覆盖了 obj1 中相同的属性
在合并配置对象、继承属性等场景下,Object.assign()方法非常实用,能够简化代码逻辑。
(4)setPrototypeOf、getPrototypeOf
Object.setPrototypeOf()和Object.getPrototypeOf()方法可直接设置和获取对象的原型。不过,由于原型链操作可能引发性能问题和代码复杂度增加,通常不建议频繁使用。
// 设置原型
Object.setPrototypeOf(school, cities);
// 参数1:给谁设置原型对象 school
// 参数2:设置哪个原型对象 cities
// 获取原型
Object.getPrototypeOf(school);
const school = {
name: "bdqn"
};
const cities = {
xiaoqu: ["北京", "上海", "深圳"]
};
// 设置原型
Object.setPrototypeOf(school, cities);
// 获取原型
Object.getPrototypeOf(school);
console.log(school);
在某些特定的面向对象编程场景中,如实现对象的继承体系时,可能会用到这两个方法,但需要谨慎操作,确保代码的性能和可读性。
2、函数扩展
2.1、箭头函数
ES6 引入了箭头函数(=>)来定义函数,这种函数表达式更加简洁。不过,箭头函数只能用于简写函数表达式,无法简写声明式函数。
function fn() {} // 不能用箭头函数简写
const fun = function () {}; // 可以简写为箭头函数
const obj = {
fn: function () {} // 可以简写为箭头函数
};
(1)语法
() => {} |
- ():用于定义函数的参数,若有多个参数,用逗号分隔。
- =>:箭头函数的标志性语法,用于连接参数和函数体。
- {}:函数体,包含具体的执行逻辑。
// ES6允许使用箭头(=>)定义函数
// 声明一个函数
// let fn = function(){}
// 等价于
let fn = (a, b) => {
return a + b;
};
// 调用函数
let result = fn(1, 2);
console.log(result);
(2)箭头函数的特性
- this 是静态的:箭头函数中的this始终指向函数声明时所在作用域下的this值,它自身没有独立的this绑定。
- 不能作为构造实例化对象:尝试使用new关键字调用箭头函数会导致报错,因为箭头函数没有prototype属性。
- 不能使用 arguments 变量:箭头函数没有自己的arguments对象,若要访问函数参数,可使用命名参数或rest参数。
- 箭头函数的简写:
- 省略小括号:当形参有且仅有一个时,可省略参数的小括号。
- 省略花括号:当函数体只有一条语句时,可省略花括号,此时return关键字也需省略,语句的执行结果将自动作为函数的返回值。
// 箭头函数的特性
// 1. this 是静态的,this 始终指向函数声明时所在作用域下的 this 值,没有自己的 this
function getName() {
console.log(this.name);
}
let getName2 = () => {
console.log(this.name);
};
// 设置 window 对象的 name 属性
window.name = "北大青鸟";
const school = {
name: "bdqn"
};
// 直接调用,this指向window
// getName();
// getName2();
// call 方法调用
// getName.call(school); // 指向school
// getName2.call(school); // 指向window
// 2. 不能作为构造实例化对象,会报错
// let Person = (name, age) => {
// this.name = name;
// this.age = age;
// };
// let me = new Person('xiao', 30);
// console.log(me);
// 3. 不能使用 arguments 变量
// let fn = () => {
// console.log(arguments);
// };
// fn(1, 2, 3);
// 4. 箭头函数的简写
// 1) 省略小括号,当形参有且只有一个的时候
let add = n => {
return n + n;
};
console.log(add(9));
// 2) 省略花括号,当代码体只有一条语句的时候,此时 return 必须省略,而且语句的执行结果就是函数的返回值
let pow = (n) => n * n;
console.log(pow(8));
注意:箭头函数不会改变this的指向,在需要保持this一致性的回调函数场景中,如事件处理函数、数组的迭代方法回调等,箭头函数是非常合适的选择。在实际应用中,应根据具体需求合理使用箭头函数,以充分发挥其简洁性和便利性。
补充 this 指向问题汇总
- 作为普通函数被调用时:this指向全局对象window(在浏览器环境中)或global(在 Node.js 环境中)。
window.age = 18;
function fn() {
console.log(this.age); // 18
}
fn();
- 对象方法里的 this:当函数作为对象的方法被调用时,this指向该对象本身。
var obj = {
age: 18,
fn: function () {
console.log(this === obj); // true
console.log(this.age); // 18
}
};
console.log(obj.fn());
- 构造函数里面的 this:在构造函数中,若没有return语句或return的是基本数据类型,this指向new创建的实例化对象;若return的是一个object对象,则最终返回该对象。
function Fn() {
this.age = 18;
// 此时a.age是return的结果20
return {
age: 20
};
}
let a = new Fn();
console.log(a.age); // 20
- call、apply、bind:在function的原型上有call、apply、bind三个方法,所有函数都是Function的实例,均可调用这三个方法来改变this的指向。
// 定义:call(thisObj,Object) 调用一个对象的一个方法,以另一个对象替换当前对象。
// 说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
function fn(x, y) {
console.log('加油');
console.log(this); // this指向window
console.log(x + y);
}
var o = {
name: 'andy'
};
fn.call(); // call 呼叫 可以调用函数
fn.call(o, 1, 2); // 第一个值是this指向,后边实参
fn.apply(o, [10, 20]); // apply传递数组
fn.call(10, 20); // this-->new Numbe(10) x-->20 y-->undefined
- 箭头函数中 this:箭头函数没有自己独立的this,它会从自己的作用域链上一层继承this。
this.age = 20;
var obj = {
age: 18,
fn: () => {
console.log(this.age); // 20
}
};
obj.fn();
2.2、 参数默认值
(1)形参初始值
在定义函数时,具有默认值的参数通常建议放在参数列表的末尾。这是一种潜规则,能避免参数解析时的混淆,提高代码的可读性和可维护性。
function add(a, b, c = 10) {
return a + b + c;
}
// let res1 = add(1, 2, 3); // 6
let res2 = add(1, 1);
console.log(res2); // 12
(2)与解构赋值结合
参数默认值可以与解构赋值一起使用,使函数参数的传递和处理更加灵活。
function connect({ name, age, sex, price = "3000" }) {
console.log(name);
console.log(age);
console.log(sex);
console.log(price);
}
connect({
name: "章三",
age: 20,
sex: "男"
// price: 4000,
});
(3)这个默认值的方式箭头函数也可以使用
箭头函数同样支持参数默认值的设置,为函数的定义和调用提供了更多便利。
const fn = (a = 10) => {
console.log(a);
};
fn(); // 不传递参数的时候,函数内部的 a 就是 10
fn(20); // 传递了参数 20 的时候,函数内部的 a 就是 20
注意:在使用箭头函数的参数默认值时,如果只有一个参数,也需要保留小括号,以确保语法的正确性。在实际编写函数时,合理设置参数默认值能提高函数的通用性和易用性,减少不必要的参数传递。
3、rest 参数
ES6 引入了rest参数,用于获取函数的实参列表,它可以替代传统的arguments对象。rest参数必须放在函数参数列表的最后。
// ES5 获取实参的方式
// function date(){
// console.log(arguments);
// }
// date('大白','二黑','三孩');
// rest 参数
function date(...args) {
console.log(args); // filter some every map
}
date("大白", "二黑", "三孩");
// rest 参数必须要放到参数最后
function fn(a, b,...args) {
console.log(a);
console.log(b);
console.log(args);
}
fn(1, 2, 3, 4, 5, 6);
注意:rest参数在处理不定个数参数的函数场景中非常实用,它将所有剩余参数收集到一个数组中,方便进行统一处理。例如在编写日志记录函数、函数柯里化等场景中,rest参数能使代码更加简洁和灵活。在实际开发中,当遇到需要处理不定数量参数的情况时,优先考虑使用rest参数能有效提升代码的质量和可维护性。
4、Symbol
(1)Symbol 基本使用
ES6 引入了一种新的原始数据类型Symbol,它表示独一无二的值。Symbol是 JavaScript 语言的第七种数据类型,类似于字符串,但具有唯一性。
// 创建方式
// 创建Symbol方式1
let s = Symbol();
// console.log(s, typeof s);
// 创建方式2
// Symbol()函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,比较容易区分
let s2 = Symbol('bdqn'); // 'bdqn'这个内容只是个标识
let s3 = Symbol('bdqn');
console.log('s2===s3', s2 === s3); // false
// 创建方式3 Symbol.for
// Symbol.for并不是每次都会创建一个新的symbol,它会先检索symbol表中是否有,没有再创建新的,有就返回上次存储的
let s4 = Symbol.for('bdqn');
let s5 = Symbol.for('bdqn');
console.log('s4===s5', s4 === s5); // true
// 注意事项
// 1: 不能与其他数据进行运算
try {
let result = s + 100;
} catch (error) {
console.error('Symbol值不能与其他数据进行运算,错误信息:', error.message);
}
(2)Symbol 创建对象属性
Symbol可以用于为对象添加唯一的属性和方法,避免属性名冲突。
// 向对象中添加方法 up down
let game = {
name: "俄罗斯方块",
up: function () {},
down: function () {}
};
// 声明一个对象
let methods = {
up: Symbol(),
down: Symbol()
};
game[methods.up] = function () {
console.log("我可以改变形状");
};
game[methods.down] = function () {
console.log("我可以快速下降!!");
};
console.log(game);
(3)Symbol 的内置对象
除了自定义的Symbol值,ES6 还提供了 11 个内置的Symbol值,它们指向语言内部使用的方法,这些方法在特定场景下会自动执行,也被称为魔术方法。
Symbol 属性 | 描述 |
Symbol.hasInstance | 当其他对象使用instanceof运算符判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的Symbol.isConcatSpreadable属性是一个布尔值,决定该对象在使用Array.prototype.concat()时是否可以展开 |
Symbol.species | 创建衍生对象时,会使用该属性来指定构造函数 |
Symbol.match | 当执行str.match(myObject)时,如果该属性存在,会调用它并返回该方法的返回值 |
Symbol.replace | 当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值 |
Symbol.search | 当该对象被str.search(myObject)方法调用时,会返回该方法的返回值 |
Symbol.split | 当该对象被str.split(myObject)方法调用时,会返回该方法的返回值 |
Symbol.iterator | 对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器 |
Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值 |
Symbol.toStringTag | 在该对象上面调用toString方法时,返回该方法的返回值,用于自定义对象的字符串表示 |
Symbol.unscopables | 该对象指定了使用with关键字时,哪些 |