}
addTwoNumbers(2, 4); // 6
addTwoNumbers(2); // 2
addTwoNumbers(); // 0
剩余参数(Rest Parameters)
在ES5中,我们这样操作一个未定义的参数:
function logArguments() {
for (var i=0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
使用休止符(…)我们可以传入大量未定义的参数:
function logArguments(…args) {
for (let arg of args) {
console.log(arg);
}
}
已命名的参数(Named Parameters)
ES5中,一种处理已命名参数的方式是使用选项方式,这种方法来自jQuery。
function initializeCanvas(options) {
var height = options.height || 600;
var width = options.width || 400;
var lineStroke = options.lineStroke || ‘black’;
}
通过解构成正式参数的方式,我们可以实现同样的功能:
function initializeCanvas(
{ height=600, width=400, lineStroke=‘black’}) {
// Use variables height, width, lineStroke here
}
如果我们想使全部参数值可选,我们可以用一个空对象这样结构:
function initializeCanvas(
{ height=600, width=400, lineStroke=‘black’} = {}) {
// …
}
展开运算符(Spread Operator)
在ES5中,查找一个array中的最大值需要使用Math.max的apply方法:
Math.max.apply(null, [-1, 100, 9001, -32]); // 9001
在es6中,我们使用展开运算符将array传递给函数作为参数:
Math.max(…[-1, 100, 9001, -32]); // 9001
我们可以用这样简洁的语法链接数组字面量:
let cities = [‘San Francisco’, ‘Los Angeles’];
let places = [‘Miami’, …cities, ‘Chicago’]; // [‘Miami’, ‘San Francisco’, ‘Los Angeles’, ‘Chicago’]
类(classes)
在ES6之前,我们通过创建构造器函数,并且在其prototype上添加属性的方法创建一个类:
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.incrementAge = function () {
return this.age += 1;
};
并且用一下方法创建继承类:
function Personal(name, age, gender, occupation, hobby) {
Person.call(this, name, age, gender);
this.occupation = occupation;
this.hobby = hobby;
}
Personal.prototype = Object.create(Person.prototype);
Personal.prototype.constructor = Personal;
Personal.prototype.incrementAge = function () {
Person.prototype.incrementAge.call(this);
this.age += 20;
console.log(this.age);
};
ES6 提供了十分有用的句法在后台实现这一切,我们可以这样直接创建一个类:
class Person {
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
incrementAge() {
this.age += 1;
}
}
并且用关键字extends
实现继承:
class Personal extends Person {
constructor(name, age, gender, occupation, hobby) {
super(name, age, gender);
this.occupation = occupation;
this.hobby = hobby;
}
incrementAge() {
super.incrementAge();
this.age += 20;
console.log(this.age);
}
}
建议:使用ES6的语法创建类模糊了后台的实现和原型如何工作,这个好特性可以使我们的代码更整洁。
Symbols
Symbol在ES6之前就已经出现了, 但是现在我们有了一个公共的接口可以直接使用。Symbol是唯一且不可改变的值,被用作哈希中的键。
Symbol()
调用Symbol()
或者Symbol(description)
会创建一个不能在全局查找的独一无二的符号。一种使用symbol()
的情况是,利用自己的逻辑修补第三方的对象或命名空间,但不确定会不会在库更新时产生冲突。例如,如果你想添加一个方法refreshCompontent
到React.Component
,并且确信这个方法他们不会在以后的更新中添加。
const refreshComponent = Symbol();
React.Component.prototype[refreshComponent] = () => {
// do something
}
###Symbol.for(key)
Symbol.for(key)
依然会创建一个唯一且不能修改的Symbol,但是它可以在全局被查找。两次调用相同的Symbol.for(key)
会创建一样的Symbol实例。注意,他和Symbol(description)
不是相同的:
Symbol(‘foo’) === Symbol(‘foo’) // false
Symbol.for(‘foo’) === Symbol(‘foo’) // false
Symbol.for(‘foo’) === Symbol.for(‘foo’) // true
一个常见的symbol方法Symbol.for(key)
是可互操作的。(使用这个方法)这个可以通过使用自己的代码在包括已知接口的第三方的对象参数中查找symbol成员实现,例如:
function reader(obj) {
const specialRead = Symbol.for(‘specialRead’);
if (obj[specialRead]) {
const reader = objspecialRead;
// do something with reader
} else {
throw new TypeError(‘object cannot be read’);
}
}
在另一个库中:
const specialRead = Symbol.for(‘specialRead’);
class SomeReadableType {
const reader = createSomeReaderFrom(this);
return reader;
}
}
ES6中,一个值得注意的是关于Symbol的互操作性的例子是Symbol.iterator
,它存在于Arrays、Strings、Generators等等的所有可迭代类型中。当作为一个方法调用的时候,它会返回一个具有迭代器接口的对象。
Maps
Maps是JavaScript中十分有用的结构。在ES6之前, 我们通过对象创建哈希maps:
var map = new Object();
map[key1] = ‘value1’;
map[key2] = ‘value2’;
但是,这样不能保护我们对已有属性以外的重写:
getOwnProperty({ hasOwnProperty: ‘Hah, overwritten’}, ‘Pwned’);
TypeError: Property ‘hasOwnProperty’ is not a function
Map允许我们使用set、get和search(等等)访问属性值。
let map = new Map();
map.set(‘name’, ‘david’);
map.get(‘name’); // david
map.has(‘name’); // true
最意想不到的是Map不再限制我们只使用字符串作为键,我们现在可以使用任何类型作为键而不会发生类型转换。
let map = new Map([
[‘name’, ‘david’],
[true, ‘false’],
[1, ‘one’],
[{}, ‘object’],
[function () {}, ‘function’]
]);
for (let key of map.keys()) {
console.log(typeof key);
// > string, boolean, number, object, function
}
注意:当使用如
map.get()
等方法测试相等的时候,诸如function和object这样的非原始值不能正常工作。因此,依然应该使用原始值(作为键),比如String、Boolean和Number。
我们也可以使用
.entries()
方法作为迭代器遍历Map
for (let [key, value] of map.entries()) {
console.log(key, value);
}
WeakMaps
ES6之前,为了保存私有数据,我们采取了很多方式。其中一个方法就是命名转换:
class Person {
constructor(age) {
this._age = age;
}
_incrementAge() {
this._age += 1;
}
}
但是命名转换会引起代码库混乱,并且不能保证总是被支持。为此,我们使用WeakMaps存储数据:
let _age = new WeakMap();
class Person {
constructor(age) {
_age.set(this, age);
}
incrementAge() {
let age = _age.get(this) + 1;
_age.set(this, age);
if (age > 50) {
console.log(‘Midlife crisis’);
}
}
}
使用WeakMap存储数据时的一个很有趣的事情是,这个key不会暴露出属性名,需要使用Reflect.ownKeys()
实现:
const person = new Person(50);
person.incrementAge(); // ‘Midlife crisis’
Reflect.ownKeys(person); // []
使用WeakMap更实际的例子是在不污染DOM自身的情况下存储与DOM元素相关的数据:
let map = new WeakMap();
let el = document.getElementById(‘someElement’);
// 给元素存一个弱引用
map.set(el, ‘reference’);
// 获得元素的值
let value = map.get(el); // ‘reference’
// 移除引用
el.parentNode.removeChild(el);
el = null;
// 元素被回收后,map是空的
如上所示,当一个对象被GC回收后,WeakMap会自动移除以其为标识符的键值对。
注意:为了进一步说明这个例子的实用性。当一个与DOM对应的对象的具有引用时,考虑jQuery如何存储它。使用WeakMaps,jQuery可以在DOM元素被删除时自动释放与之关联的内存。总而言之,对任何库而言,WeakMaps对操作DOM元素是非常实用的。
Promises
Promise允许我们把水平的代码(回调函数的地狱):
func1(function (value1) {
func2(value1, function (value2) {
func3(value2, function (value3) {
func4(value3, function (value4) {
func5(value4, function (value5) {
// Do something with value 5
});
});
});
});
});
转换为竖直的代码:
func1(value1)
.then(func2)
.then(func3)
.then(func4)
.then(func5, value5 => {
// Do something with value 5
});
在ES6之前,我们使用bluebird或是Q,现在我们有了Promises:
new Promise((resolve, reject) =>
reject(new Error(‘Failed to fulfill Promise’)))
.catch(reason => console.log(reason));
这里我们有2个handlers,resolve(Promise执行成功时调用的函数)和reject(Promise失败时调用的函数)。
使用Promise的好处:使用嵌套的回调函数处理错误会很混乱。使用Promise,我们可以很清晰的使错误冒泡,并且就近处理它们。更好的是,在它处理成功(或失败)之后Promise的值是不可修改的。
以下是个使用Promise的实例:
var request = require(‘request’);
return new Promise((resolve, reject) => {
request.get(url, (error, response, body) => {
if (body) {
resolve(JSON.parse(body));
} else {
resolve({});
}
});
});
我们可以使用Promise.all()
并行的处理一个异步操作数组:
let urls = [
‘/api/commits’,
‘/api/issues/opened’,
‘/api/issues/assigned’,
‘/api/issues/completed’,
‘/api/issues/comments’,
‘/api/pullrequests’
];
let promises = urls.map((url) => {
return new Promise((resolve, reject) => {
$.ajax({ url: url })
.done((data) => {
resolve(data);
});
});
});
Promise.all(promises)
.then((results) => {
// Do something with results of all our promises
});
Generators
和Promise使我们避免回调函数的地狱相似,Generators可以扁平化我们的代码——给我们一种同步执行异步代码的感觉,Generators是个很重要的函数,它使我们可以暂停操作的执行,随后返回表达式的值。
下面是使用Generator的一个简单例子:
function* sillyGenerator() {
yield 1;
yield 2;
yield 3;
yield 4;
}
var generator = sillyGenerator();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: 4, done: false }
这里,next()
使我们generator继续推进,并且得到新的表达式的值(译注:每次推进到下一个yield值)。当然,上面的例子很牵强,我们可以利用Generator以同步的方式写异步代码:
// 利用Generator屏蔽异步过程
function request(url) {
getJSON(url, function(response) {
generator.next(response);
});
}
下面我们写一个generator函数用来返回我们自己的数据:
function* getData() {
var entry1 = yield request(‘http://some_api/item1’);
var data1 = JSON.parse(entry1);
var entry2 = yield request(‘http://some_api/item2’);
var data2 = JSON.parse(entry2);
}
利用yield的功能,我们确保entry1可以获得返回的数据,用于解析并存储到data1中。
当我们利用generator以同步的方式写异步的代码时,其中的错误不会简单清晰的传递。因此,我们利用Promise加强generator:
function request(url) {
return new Promise((resolve, reject) => {
getJSON(url, resolve);
});
}
我们写了一个函数,利用next
用来按序地一步步遍历generator。该函数利用上述的请求方式并yeild一个Promise(对象)。
function iterateGenerator(gen) {
var generator = gen();
(function iterate(val) {
var ret = generator.next();
if(!ret.done) {
ret.value.then(iterate);
}
})();
}
通过Promise加强generator后,我们可以利用Promise.catch和Promise.reject这样清晰的方式传播错误。只用这个加强版的Generator和以前一样简单:
iterateGenerator(function* getData() {
var entry1 = yield request(‘http://some_api/item1’);
var data1 = JSON.parse(entry1);
var entry2 = yield request(‘http://some_api/item2’);
var data2 = JSON.parse(entry2);
});
我们可以重用写好的代码,像过去使用Generator一样,这一点很强大。当我们利用generator以同步的方式写异步的代码的同时,利用一个不错的方式保留了错误传播的能力,我们实际上可以利用一个更为简单的方式达到同样的效果:异步等待(Async-Await)。
Async Await
这是一个在ES2016(ES7)中即将有的特性,async await允许我们更简单地使用Generator和Promise执行和已完成工作相同的任务:
var request = require(‘request’);
function getJSON(url) {
return new Promise(function(resolve, reject) {
request(url, function(error, response, body) {
resolve(body);
});
});
}
async function main() {
var data = await getJSON();
console.log(data); // NOT undefined!
}
main();
在后台,它的实现类似Generators。我(作者)强烈建议使用这个替代Generators + Promises。还会有很多的资源出现并使用ES7,同时,Babel也会用在这里。
Getter 和 Setter 函数
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
更多面试题
**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
});
}
async function main() {
var data = await getJSON();
console.log(data); // NOT undefined!
}
main();
在后台,它的实现类似Generators。我(作者)强烈建议使用这个替代Generators + Promises。还会有很多的资源出现并使用ES7,同时,Babel也会用在这里。
Getter 和 Setter 函数
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-HVh1salR-1710592440505)]
[外链图片转存中…(img-UOzTqZzK-1710592440506)]
[外链图片转存中…(img-eyB10c36-1710592440507)]
[外链图片转存中…(img-qR02Ggsq-1710592440507)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-OnUDVdPO-1710592440508)]
更多面试题
**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
[外链图片转存中…(img-1sA2JIRR-1710592440508)]