做者:Mark Ajavascript
译者:前端小智前端
来源:devjava
点赞再看,养成习惯react
本文
GitHub
github.com/qq449245884… 上已经收录,更多往期高赞文章的分类,也整理了不少个人文档,和教程资料。欢迎Star和完善,你们面试能够参照考点复习,但愿咱们一块儿有点东西。git
因为篇幅过长,我将此系列分红上中下三篇,上篇:github
看完这几道 JavaScript 面试题,让你与考官对答如流(上)面试
Array.prototype.map
方法Array.prototype.filter
方法Array.prototype.reduce
方法b
会变成一个全局变量?var
,let
和const
的区别是什么Set
对象,它是如何工做的?IIFE或当即调用的函数表达式是在建立或声明后将被调用或执行的函数。 建立IIFE的语法是,将function (){}
包裹在在括号()
内,而后再用另外一个括号()
调用它,如:(function(){})()
编程
(function(){
...
} ());
(function () {
...
})();
(function named(params) {
...
})();
(() => {
});
(function (global) {
...
})(window);
const utility = (function () {
return {
...
}
})
复制代码
这些示例都是有效的IIFE。 倒数第二个救命代表咱们能够将参数传递给IIFE函数。 最后一个示例代表,咱们能够将IIFE
的结果保存到变量中,以便稍后使用。数组
IIFE的一个主要做用是避免与全局做用域内的其余变量命名冲突或污染全局命名空间,来个例子。promise
<script src="https://cdnurl.com/somelibrary.js"></script>
复制代码
假设咱们引入了一个omelibr.js
的连接,它提供了一些咱们在代码中使用的全局函数,可是这个库有两个方法咱们没有使用:createGraph
和drawGraph
,由于这些方法都有bug
。咱们想实现本身的createGraph
和drawGraph
方法。
解决此问题的一种方法是直接覆盖:
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
function createGraph() {
// createGraph logic here
}
function drawGraph() {
// drawGraph logic here
}
</script>
复制代码
当咱们使用这个解决方案时,咱们覆盖了库提供给咱们的那两个方法。
另外一种方式是咱们本身更名称:
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
function myCreateGraph() {
// createGraph logic here
}
function myDrawGraph() {
// drawGraph logic here
}
</script>
复制代码
当咱们使用这个解决方案时,咱们把那些函数调用更改成新的函数名。
还有一种方法就是使用IIFE:
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
const graphUtility = (function () {
function createGraph() {
// createGraph logic here
}
function drawGraph() {
// drawGraph logic here
}
return {
createGraph,
drawGraph
}
})
</script>
复制代码
在此解决方案中,咱们要声明了graphUtility
变量,用来保存IIFE执行的结果,该函数返回一个包含两个方法createGraph
和drawGraph
的对象。
IIFE 还能够用来解决一个常见的面试题:
var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
li[i].addEventListener('click', function (e) {
console.log(i);
})
复制代码
假设咱们有一个带有list-group
类的ul
元素,它有5
个li
子元素。 当咱们单击单个li
元素时,打印对应的下标值。但在此外上述代码不起做用,这里每次点击 li
打印 i
的值都是5
,这是因为闭包的缘由。
闭包只是函数记住其当前做用域,父函数做用域和全局做用域的变量引用的能力。 当咱们在全局做用域内使用var
关键字声明变量时,就建立全局变量i
。 所以,当咱们单击li
元素时,它将打印5
,由于这是稍后在回调函数中引用它时i
的值。
使用 IIFE 能够解决此问题:
var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
(function (currentIndex) {
li[currentIndex].addEventListener('click', function (e) {
console.log(currentIndex);
})
})(i);
}
复制代码
该解决方案之因此行的通,是由于IIFE会为每次迭代建立一个新的做用域,咱们捕获i
的值并将其传递给currentIndex
参数,所以调用IIFE时,每次迭代的currentIndex
值都是不一样的。
apply()
方法调用一个具备给定this值的函数,以及做为一个数组(或相似数组对象)提供的参数。
const details = {
message: 'Hello World!'
};
function getMessage(){
return this.message;
}
getMessage.apply(details); // 'Hello World!'
复制代码
call()
方法的做用和apply()
方法相似,区别就是call()
方法接受的是参数列表,而apply()
方法接受的是一个参数数组。
const person = {
name: "Marko Polo"
};
function greeting(greetingMessage) {
return `${greetingMessage} ${this.name}`;
}
greeting.apply(person, ['Hello']); // "Hello Marko Polo!"
复制代码
Function.prototype.call
方法的用途是什么?call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
const details = {
message: 'Hello World!'
};
function getMessage(){
return this.message;
}
getMessage.call(details); // 'Hello World!'
复制代码
注意:该方法的语法和做用与 apply()
方法相似,只有一个区别,就是 call()
方法接受的是一个参数列表,而 apply()
方法接受的是一个包含多个参数的数组。
const person = {
name: "Marko Polo"
};
function greeting(greetingMessage) {
return `${greetingMessage} ${this.name}`;
}
greeting.call(person, 'Hello'); // "Hello Marko Polo!"
复制代码
apply()
方法能够在使用一个指定的 this
值和一个参数数组(或类数组对象)的前提下调用某个函数或方法。call()
方法相似于apply()
,不一样之处仅仅是call()
接受的参数是参数列表。
const obj1 = {
result:0
};
const obj2 = {
result:0
};
function reduceAdd(){
let result = 0;
for(let i = 0, len = arguments.length; i < len; i++){
result += arguments[i];
}
this.result = result;
}
reduceAdd.apply(obj1, [1, 2, 3, 4, 5]); // 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 15
复制代码
bind()
方法建立一个新的函数,在 bind()
被调用时,这个新函数的 this
被指定为 bind()
的第一个参数,而其他参数将做为新函数的参数,供调用时使用。
import React from 'react';
class MyComponent extends React.Component {
constructor(props){
super(props);
this.state = {
value : ""
}
this.handleChange = this.handleChange.bind(this);
// 将 “handleChange” 方法绑定到 “MyComponent” 组件
}
handleChange(e){
//do something amazing here
}
render(){
return (
<>
<input type={this.props.type}
value={this.state.value}
onChange={this.handleChange}
/>
</>
)
}
}
复制代码
函数式编程(一般缩写为FP)是经过编写纯函数,避免共享状态、可变数据、反作用 来构建软件的过程。数式编程是声明式 的而不是命令式 的,应用程序的状态是经过纯函数流动的。与面向对象编程造成对比,面向对象中应用程序的状态一般与对象中的方法共享和共处。
函数式编程是一种编程范式 ,这意味着它是一种基于一些基本的定义原则(如上所列)思考软件构建的方式。固然,编程范示的其余示例也包括面向对象编程和过程编程。
函数式的代码每每比命令式或面向对象的代码更简洁,更可预测,更容易测试 - 但若是不熟悉它以及与之相关的常见模式,函数式的代码也可能看起来更密集杂乱,而且 相关文献对新人来讲是很差理解的。
JavaScript支持闭包和高阶函数是函数式编程语言的特色。
高阶函数只是将函数做为参数或返回值的函数。
function higherOrderFunction(param,callback){
return callback(param);
}
复制代码
在JavaScript中,函数不只拥有一切传统函数的使用方式(声明和调用),并且能够作到像简单值同样赋值(var func = function(){})
、传参(function func(x,callback){callback();})
、返回(function(){return function(){}})
,这样的函数也称之为第一级函数(First-class Function)。不只如此,JavaScript中的函数还充当了类的构造函数的做用,同时又是一个Function
类的实例(instance)。这样的多重身份让JavaScript的函数变得很是重要。
Array.prototype.map 方法
map()
方法建立一个新数组,其结果是该数组中的每一个元素都调用一个提供的函数后返回的结果。
function map(arr, mapCallback) {
// 首先,检查传递的参数是否正确。
if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
return [];
} else {
let result = [];
// 每次调用此函数时,咱们都会建立一个 result 数组
// 由于咱们不想改变原始数组。
for (let i = 0, len = arr.length; i < len; i++) {
result.push(mapCallback(arr[i], i, arr));
// 将 mapCallback 返回的结果 push 到 result 数组中
}
return result;
}
}
复制代码
Array.prototype.filter
方法filter()
方法建立一个新数组, 其包含经过所提供函数实现的测试的全部元素。
function filter(arr, filterCallback) {
// 首先,检查传递的参数是否正确。
if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function')
{
return [];
} else {
let result = [];
// 每次调用此函数时,咱们都会建立一个 result 数组
// 由于咱们不想改变原始数组。
for (let i = 0, len = arr.length; i < len; i++) {
// 检查 filterCallback 的返回值是不是真值
if (filterCallback(arr[i], i, arr)) {
// 若是条件为真,则将数组元素 push 到 result 中
result.push(arr[i]);
}
}
return result; // return the result array
}
}
复制代码
Array.prototype.reduce
方法reduce()
方法对数组中的每一个元素执行一个由您提供的reducer
函数(升序执行),将其结果汇总为单个返回值。
function reduce(arr, reduceCallback, initialValue) {
// 首先,检查传递的参数是否正确。
if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function')
{
return [];
} else {
// 若是没有将initialValue传递给该函数,咱们将使用第一个数组项做为initialValue
let hasInitialValue = initialValue !== undefined;
let value = hasInitialValue ? initialValue : arr[0];
、
// 若是有传递 initialValue,则索引从 1 开始,不然从 0 开始
for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) {
value = reduceCallback(value, arr[i], i, arr);
}
return value;
}
}
复制代码
arguments
对象是函数中传递的参数值的集合。它是一个相似数组的对象,由于它有一个length属性,咱们可使用数组索引表示法arguments[1]
来访问单个值,但它没有数组中的内置方法,如:forEach
、reduce
、filter
和map
。
咱们可使用Array.prototype.slice
将arguments
对象转换成一个数组。
function one() {
return Array.prototype.slice.call(arguments);
}
复制代码
注意:箭头函数中没有arguments
对象。
function one() {
return arguments;
}
const two = function () {
return arguments;
}
const three = function three() {
return arguments;
}
const four = () => arguments;
four(); // Throws an error - arguments is not defined
复制代码
当咱们调用函数four
时,它会抛出一个ReferenceError: arguments is not defined error
。使用rest
语法,能够解决这个问题。
const four = (...args) => args;
复制代码
这会自动将全部参数值放入数组中。
咱们可使用Object.create
方法建立没有原型的对象。
const o1 = {};
console.log(o1.toString()); // [object Object]
const o2 = Object.create(null);
console.log(o2.toString());
// throws an error o2.toString is not a function
复制代码
b
会变成一个全局变量?function myFunc() {
let a = b = 0;
}
myFunc();
复制代码
缘由是赋值运算符是从右到左的求值的。这意味着当多个赋值运算符出如今一个表达式中时,它们是从右向左求值的。因此上面代码变成了这样:
function myFunc() {
let a = (b = 0);
}
myFunc();
复制代码
首先,表达式b = 0
求值,在本例中b
没有声明。所以,JS引擎在这个函数外建立了一个全局变量b
,以后表达式b = 0
的返回值为0
,并赋给新的局部变量a
。
咱们能够经过在赋值以前先声明变量来解决这个问题。
function myFunc() {
let a,b;
a = b = 0;
}
myFunc();
复制代码
ECMAScript 是编写脚本语言的标准,这意味着JavaScript遵循ECMAScript标准中的规范变化,由于它是JavaScript的蓝图。
ECMAScript 和 Javascript,本质上都跟一门语言有关,一个是语言自己的名字,一个是语言的约束条件 只不过发明JavaScript的那我的(Netscape公司),把东西交给了ECMA(European Computer Manufacturers Association),这我的规定一下他的标准,由于当时有java语言了,又想强调这个东西是让ECMA这我的定的规则,因此就这样一个神奇的东西诞生了,这个东西的名称就叫作ECMAScript。
javaScript = ECMAScript + DOM + BOM(自认为是一种广义的JavaScript)
ECMAScript说什么JavaScript就得作什么!
JavaScript(狭义的JavaScript)作什么都要问问ECMAScript我能不能这样干!若是不能我就错了!能我就是对的!
——忽然感受JavaScript好没有尊严,为啥要搞我的出来约束本身,
那我的被创造出来也好委屈,本身被创造出来彻底是由于要约束JavaScript。
var
,let
和const
的区别是什么?var
声明的变量会挂载在window
上,而let
和const
声明的变量不会:
var a = 100;
console.log(a,window.a); // 100 100
let b = 10;
console.log(b,window.b); // 10 undefined
const c = 1;
console.log(c,window.c); // 1 undefined
复制代码
var
声明变量存在变量提高,let
和const
不存在变量提高:
console.log(a); // undefined ===> a已声明还没赋值,默认获得undefined值
var a = 100;
console.log(b); // 报错:b is not defined ===> 找不到b这个变量
let b = 10;
console.log(c); // 报错:c is not defined ===> 找不到c这个变量
const c = 10;
复制代码
let
和const
声明造成块做用域
if(1){
var a = 100;
let b = 10;
}
console.log(a); // 100
console.log(b) // 报错:b is not defined ===> 找不到b这个变量
-------------------------------------------------------------
if(1){
var a = 100;
const c = 1;
}
console.log(a); // 100
console.log(c) // 报错:c is not defined ===> 找不到c这个变量
复制代码
同一做用域下let
和const
不能声明同名变量,而var
能够
var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
-------------------------------------
let a = 100;
let a = 10;
// 控制台报错:Identifier 'a' has already been declared ===> 标识符a已经被声明了。
复制代码
暂存死区
var a = 100;
if(1){
a = 10;
//在当前块做用域中存在a使用let/const声明的状况下,给a赋值10时,只会在当前做用域找变量a,
// 而这时,还未到声明时候,因此控制台Error:a is not defined
let a = 1;
}
复制代码
const
/*
*   一、一旦声明必须赋值,不能使用null占位。
*
*   二、声明后不能再修改
*
*   三、若是声明的是复合类型数据,能够修改其属性
*
* */
const a = 100;
const list = [];
list[0] = 10;
console.log(list);  // [10]
const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj);  // {a:10000,name:'apple'}
复制代码
箭头函数表达式的语法比函数表达式更简洁,而且没有本身的this
,arguments
,super
或new.target
。箭头函数表达式更适用于那些原本须要匿名函数的地方,而且它不能用做构造函数。
//ES5 Version
var getCurrentDate = function (){
return new Date();
}
//ES6 Version
const getCurrentDate = () => new Date();
复制代码
在本例中,ES5 版本中有function(){}
声明和return
关键字,这两个关键字分别是建立函数和返回值所须要的。在箭头函数版本中,咱们只须要()
括号,不须要 return
语句,由于若是咱们只有一个表达式或值须要返回,箭头函数就会有一个隐式的返回。
//ES5 Version
function greet(name) {
return 'Hello ' + name + '!';
}
//ES6 Version
const greet = (name) => `Hello ${name}`;
const greet2 = name => `Hello ${name}`;
复制代码
咱们还能够在箭头函数中使用与函数表达式和函数声明相同的参数。若是咱们在一个箭头函数中有一个参数,则能够省略括号。
const getArgs = () => arguments
const getArgs2 = (...rest) => rest
复制代码
箭头函数不能访问arguments
对象。因此调用第一个getArgs
函数会抛出一个错误。相反,咱们可使用rest参数来得到在箭头函数中传递的全部参数。
const data = {
result: 0,
nums: [1, 2, 3, 4, 5],
computeResult() {
// 这里的“this”指的是“data”对象
const addAll = () => {
return this.nums.reduce((total, cur) => total + cur, 0)
};
this.result = addAll();
}
};
复制代码
箭头函数没有本身的this
值。 它捕获词法做用域函数的this
值,在此示例中,addAll
函数将复制computeResult
方法中的this
值,若是咱们在全局做用域声明箭头函数,则this
值为 window
对象。
类(class)
是在 JS 中编写构造函数的新方法。它是使用构造函数的语法糖,在底层中使用仍然是原型和基于原型的继承。
//ES5 Version
function Person(firstName, lastName, age, address){
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
}
Person.self = function(){
return this;
}
Person.prototype.toString = function(){
return "[object Person]";
}
Person.prototype.getFullName = function (){
return this.firstName + " " + this.lastName;
}
//ES6 Version
class Person {
constructor(firstName, lastName, age, address){
this.lastName = lastName;
this.firstName = firstName;
this.age = age;
this.address = address;
}
static self() {
return this;
}
toString(){
return "[object Person]";
}
getFullName(){
return `${this.firstName} ${this.lastName}`;
}
}
复制代码
重写方法并从另外一个类继承。
//ES5 Version
Employee.prototype = Object.create(Person.prototype);
function Employee(firstName, lastName, age, address, jobTitle, yearStarted) {
Person.call(this, firstName, lastName, age, address);
this.jobTitle = jobTitle;
this.yearStarted = yearStarted;
}
Employee.prototype.describe = function () {
return `I am ${this.getFullName()} and I have a position of ${this.jobTitle} and I started at ${this.yearStarted}`;
}
Employee.prototype.toString = function () {
return "[object Employee]";
}
//ES6 Version
class Employee extends Person { //Inherits from "Person" class
constructor(firstName, lastName, age, address, jobTitle, yearStarted) {
super(firstName, lastName, age, address);
this.jobTitle = jobTitle;
this.yearStarted = yearStarted;
}
describe() {
return `I am ${this.getFullName()} and I have a position of ${this.jobTitle} and I started at ${this.yearStarted}`;
}
toString() { // Overriding the "toString" method of "Person"
return "[object Employee]";
}
}
复制代码
因此咱们要怎么知道它在内部使用原型?
class Something {
}
function AnotherSomething(){
}
const as = new AnotherSomething();
const s = new Something();
console.log(typeof Something); // "function"
console.log(typeof AnotherSomething); // "function"
console.log(as.toString()); // "[object Object]"
console.log(as.toString()); // "[object Object]"
console.log(as.toString === Object.prototype.toString); // true
console.log(s.toString === Object.prototype.toString); // true
复制代码
模板字符串是在 JS 中建立字符串的一种新方法。咱们能够经过使用反引号使模板字符串化。
//ES5 Version
var greet = 'Hi I\'m Mark';
//ES6 Version
let greet = `Hi I'm Mark`;
复制代码
在 ES5 中咱们须要使用一些转义字符来达到多行的效果,在模板字符串不须要这么麻烦:
//ES5 Version
var lastWords = '\n'
+ ' I \n'
+ ' Am \n'
+ 'Iron Man \n';
//ES6 Version
let lastWords = `
I
Am
Iron Man
`;
复制代码
在ES5版本中,咱们须要添加\n
以在字符串中添加新行。 在模板字符串中,咱们不须要这样作。
//ES5 Version
function greet(name) {
return 'Hello ' + name + '!';
}
//ES6 Version
function greet(name) {
return `Hello ${name} !`;
}
复制代码
在 ES5 版本中,若是须要在字符串中添加表达式或值,则须要使用+
运算符。 在模板字符串s中,咱们可使用${expr}
嵌入一个表达式,这使其比 ES5 版本更整洁。
对象析构是从对象或数组中获取或提取值的一种新的、更简洁的方法。假设有以下的对象:
const employee = {
firstName: "Marko",
lastName: "Polo",
position: "Software Developer",
yearHired: 2017
};
复制代码
从对象获取属性,早期方法是建立一个与对象属性同名的变量。这种方法很麻烦,由于咱们要为每一个属性建立一个新变量。假设咱们有一个大对象,它有不少属性和方法,用这种方法提取属性会很麻烦。
var firstName = employee.firstName;
var lastName = employee.lastName;
var position = employee.position;
var yearHired = employee.yearHired;
复制代码
使用解构方式语法就变得简洁多了:
{ firstName, lastName, position, yearHired } = employee;
复制代码
咱们还能够为属性取别名:
let { firstName: fName, lastName: lName, position, yearHired } = employee;
复制代码
固然若是属性值为 undefined
时,咱们还能够指定默认值:
let { firstName = "Mark", lastName: lName, position, yearHired } = employee;
复制代码
模块使咱们可以将代码基础分割成多个文件,以得到更高的可维护性,而且避免将全部代码放在一个大文件中。在 ES6 支持模块以前,有两个流行的模块。
基本上,使用模块的方式很简单,import
用于从另外一个文件中获取功能或几个功能或值,同时export
用于从文件中公开功能或几个功能或值。
导出
使用 ES5 (CommonJS)
// 使用 ES5 CommonJS - helpers.js
exports.isNull = function (val) {
return val === null;
}
exports.isUndefined = function (val) {
return val === undefined;
}
exports.isNullOrUndefined = function (val) {
return exports.isNull(val) || exports.isUndefined(val);
}
复制代码
使用 ES6 模块
// 使用 ES6 Modules - helpers.js
export function isNull(val){
return val === null;
}
export function isUndefined(val) {
return val === undefined;
}
export function isNullOrUndefined(val) {
return isNull(val) || isUndefined(val);
}
复制代码
在另外一个文件中导入函数
// 使用 ES5 (CommonJS) - index.js
const helpers = require('./helpers.js'); // helpers is an object
const isNull = helpers.isNull;
const isUndefined = helpers.isUndefined;
const isNullOrUndefined = helpers.isNullOrUndefined;
// or if your environment supports Destructuring
const { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js');
-------------------------------------------------------
// ES6 Modules - index.js
import * as helpers from './helpers.js'; // helpers is an object
// or
import { isNull, isUndefined, isNullOrUndefined as isValid } from './helpers.js';
// using "as" for renaming named exports
复制代码
在文件中导出单个功能或默认导出
使用 ES5 (CommonJS)
// 使用 ES5 (CommonJS) - index.js
class Helpers {
static isNull(val) {
return val === null;
}
static isUndefined(val) {
return val === undefined;
}
static isNullOrUndefined(val) {
return this.isNull(val) || this.isUndefined(val);
}
}
module.exports = Helpers;
复制代码
使用ES6 Modules
// 使用 ES6 Modules - helpers.js
class Helpers {
static isNull(val) {
return val === null;
}
static isUndefined(val) {
return val === undefined;
}
static isNullOrUndefined(val) {
return this.isNull(val) || this.isUndefined(val);
}
}
export default Helpers
复制代码
从另外一个文件导入单个功能
使用ES5 (CommonJS)
// 使用 ES5 (CommonJS) - index.js
const Helpers = require('./helpers.js');
console.log(Helpers.isNull(null));
复制代码
使用 ES6 Modules
import Helpers from '.helpers.js'
console.log(Helpers.isNull(null));
复制代码
Set
对象,它是如何工做的?Set 对象容许你存储任何类型的惟一值,不管是原始值或者是对象引用。
咱们可使用Set
构造函数建立Set
实例。
const set1 = new Set();
const set2 = new Set(["a","b","c","d","d","e"]);
复制代码
咱们可使用add
方法向Set
实例中添加一个新值,由于add
方法返回Set
对象,因此咱们能够以链式的方式再次使用add
。若是一个值已经存在于Set
对象中,那么它将再也不被添加。
set2.add("f");
set2.add("g").add("h").add("i").add("j").add("k").add("k");
// 后一个“k”不会被添加到set对象中,由于它已经存在了
复制代码
咱们可使用has
方法检查Set
实例中是否存在特定的值。
set2.has("a") // true
set2.has("z") // true
复制代码
咱们可使用size
属性得到Set
实例的长度。
set2.size // returns 10
复制代码
可使用clear
方法删除 Set
中的数据。
set2.clear();
复制代码
咱们可使用Set
对象来删除数组中重复的元素。
const numbers = [1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 5];
const uniqueNums = [...new Set(numbers)]; // [1,2,3,4,5,6,7,8]
复制代码
回调函数是一段可执行的代码段,它做为一个参数传递给其余的代码,其做用是在须要的时候方便调用这段(回调函数)代码。
在JavaScript中函数也是对象的一种,一样对象能够做为参数传递给函数,所以函数也能够做为参数传递给另一个函数,这个做为参数的函数就是回调函数。
const btnAdd = document.getElementById('btnAdd');
btnAdd.addEventListener('click', function clickCallback(e) {
// do something useless
});
复制代码
在本例中,咱们等待id
为btnAdd
的元素中的click
事件,若是它被单击,则执行clickCallback
函数。回调函数向某些数据或事件添加一些功能。
数组中的reduce
、filter
和map
方法须要一个回调做为参数。回调的一个很好的类比是,当你打电话给某人,若是他们不接,你留下一条消息,你期待他们回调。调用某人或留下消息的行为是事件或数据,回调是你但愿稍后发生的操做。
Promise 是异步编程的一种解决方案:从语法上讲,promise
是一个对象,从它能够获取异步操做的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise
有三种状态:pending(等待态)
,fulfiled(成功态)
,rejected(失败态)
;状态一旦改变,就不会再变。创造promise
实例后,它会当即执行。
fs.readFile('somefile.txt', function (e, data) {
if (e) {
console.log(e);
}
console.log(data);
});
复制代码
若是咱们在回调内部有另外一个异步操做,则此方法存在问题。 咱们将有一个混乱且不可读的代码。 此代码称为**“回调地狱”**。
// 回调地狱
fs.readFile('somefile.txt', function (e, data) {
//your code here
fs.readdir('directory', function (e, files) {
//your code here
fs.mkdir('directory', function (e) {
//your code here
})
})
})
复制代码
若是咱们在这段代码中使用promise
,它将更易于阅读、理解和维护。
promReadFile('file/path')
.then(data => {
return promReaddir('directory');
})
.then(data => {
return promMkdir('directory');
})
.catch(e => {
console.log(e);
})
复制代码
promise
有三种不一样的状态:
pending
状态的 Promise
对象会触发 fulfilled/rejected
状态,在其状态处理方法中能够传入参数/失败信息。当操做成功完成时,Promise 对象的 then
方法就会被调用;不然就会触发 catch
。如:
const myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve("成功!");
}, 250);
});
myFirstPromise.then((data) => {
console.log("Yay! " + data);
}).catch((e) => {...});
复制代码
因为篇幅过长,我将此系列分红上中下三篇,下篇咱们在见。
代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
原文:
干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。
我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,便可看到福利,你懂的。