1.undefined 和 null 有什么区别?undefined 和null 之间的差异之前,我们先来看看它们的相似类。let primitiveTypes = ['string','number','null','undefined','boolean','symbol', 'bigint']; Boolean(value) 或!!value 将其转换为布尔值时,值为false 。
undefined 是未指定特定值的变量的默认值,或者没有显式返回值的函数,如:console.log(1) ,还包括对象中不存在的属性,这些 JS 引擎都会为其分配 undefined 值。let _thisIsUndefined; const doNothing = () => {}; const someObj = { a : 'ay', b : 'bee', c : 'si' };
console.log(_thisIsUndefined); // undefined console.log(doNothing()); // undefined console.log(someObj['d']); // undefined null 是“不代表任何值的值”。 null 是已明确定义给变量的值。在此示例中,当fs.readFile 方法未引发错误时,我们将获得null 值。
null 和undefined 时,我们使用== 时得到true ,使用=== 时得到false : console.log(null == undefined); // true console.log(null === undefined); // false 2. && 运算符能做什么&& 也可以叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。
if 语句const router: Router = Router();
router.get('/endpoint', (req: Request, res: Response) => { let conMobile: PoolConnection; try { //do some db operations } catch (e) { if (conMobile) { conMobile.release(); } } }); && 操作符
3. || 运算符能做什么|| 也叫或逻辑或 ,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。console.log(null || 1 || undefined); // 1
function logName(name) { var n = name || 'Mark'; console.log(n); }
logName(); // 'Mark' 4. 使用 + 或一元加运算符是将字符串转换为数字的最快方法吗?+ 是将字符串转换为数字的最快方法,因为如果值已经是数字,它不会执行任何操作。5. DOM 是什么?
document 对象表示DOM。它为我们提供了许多方法,我们可以使用这些方法来选择元素来更新元素内容,等等。6. 什么是事件传播?window 为止;而在“捕获阶段”中,事件从window 开始向下触发元素 事件或event.target 。
7. 什么是事件冒泡?window 为止。<div class='grandparent'> <div class='parent'> <div class='child'>1</div> </div> </div>
addEventListener 方法具有第三个可选参数useCapture ,其默认值为false ,事件将在冒泡阶段中发生,如果为true ,则事件将在捕获阶段中发生。如果单击child 元素,它将分别在控制台上记录child ,parent ,grandparent ,html ,document 和window ,这就是事件冒泡。8. 什么是事件捕获?window 开始,一直到触发事件的元素。<div class='grandparent'> <div class='parent'> <div class='child'>1</div> </div> </div>
addEventListener 方法具有第三个可选参数useCapture ,其默认值为false ,事件将在冒泡阶段中发生,如果为true ,则事件将在捕获阶段中发生。如果单击child 元素,它将分别在控制台上打印window ,document ,html ,grandparent 和parent ,这就是事件捕获。9. event.preventDefault() 和 event.stopPropagation()方法之间有什么区别?event.preventDefault() 方法可防止元素的默认行为。如果在表单元素中使用,它将阻止其提交。如果在锚元素中使用,它将阻止其导航。如果在上下文菜单中使用,它将阻止其显示或显示。 event.stopPropagation() 方法用于阻止捕获和冒泡阶段中当前事件的进一步传播。10. 如何知道是否在元素中使用了 |
x | y | x==y |
true
。条件1
,因为x
和y
具有相同的类型和值。条件4
,在比较之前将y
转换为数字。条件2
。条件7
,因为y
是boolean
类型。条件8
。使用toString()
方法将数组转换为字符串,该方法返回1,2
。条件8
。使用toString()
方法将对象转换为字符串,该方法返回[object Object]
。x | y | x ===y |
===
运算符,则第一个示例以外的所有比较将返回false
,因为它们的类型不同,而第一个示例将返回true
,因为两者的类型和值相同。let a = { a: 1 };
let b = { a: 1 };
let c = a;
console.log(a === b); // 打印 false,即使它们有相同的属性
console.log(a === c); // true
console.log
语句返回false
,而第二个console.log
语句返回true
。a
和c
有相同的引用地址,而a
和b
没有。!!
运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法。console.log(!!null); // false
console.log(!!undefined); // false
console.log(!!''); // false
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!' '); // true
console.log(!!{}); // true
console.log(!![]); // true
console.log(!!1); // true
console.log(!![].length); // false
逗号
运算符在一行中计算多个表达式。它从左到右求值,并返回右边最后一个项目或最后一个操作数的值。let x = 5;
x = (x++ , x = addFive(x), x *= 2, x -= 5, x += 10);
function addFive(num) {
return num + 5;
}
x
的值为27
。首先,我们将x
的值增加到6
,然后调用函数addFive(6)
并将6
作为参数传递并将结果重新分配给x
,此时x
的值为11
。之后,将x
的当前值乘以2
并将其分配给x
,x
的更新值为22
。然后,将x
的当前值减去5
并将结果分配给x
x
更新后的值为17
。最后,我们将x
的值增加10
,然后将更新的值分配给x
,最终x
的值为27
。编译
和执行
。var
关键字进行声明),还会为它们提供默认值: undefined
。var
声明的变量,或者函数声明才会被提升,相反,函数表达式或箭头函数,let
和const
声明的变量,这些都不会被提升。console.log(y);
y = 1;
console.log(y);
console.log(greet('Mark'));
function greet(name){
return 'Hello ' + name + '!';
}
var y;
undefined
,1
, Hello Mark!
。function greet(name) {
return 'Hello ' + name + '!';
}
var y; // 默认值 undefined
// 等待“编译”阶段完成,然后开始“执行”阶段
/*
console.log(y);
y = 1;
console.log(y);
console.log(greet('Mark'));
*/
function greet(name) {
return 'Hello ' + name + '!';
}
var y;
//start 'execution' phase
console.log(y);
y = 1;
console.log(y);
console.log(greet('Mark'));
全局作用域——在全局命名空间中声明的变量或函数位于全局作用域中,因此在代码中的任何地方都可以访问它们。
//global namespace
var g = 'global';
function globalFunc(){
function innerFunc(){
console.log(g);
// can access 'g' because 'g' is a global variable
}
innerFunc();
}
函数作用域——在函数中声明的变量、函数和参数可以在函数内部访问,但不能在函数外部访问。
function myFavoriteFunc(a) {
if (true) {
var b = 'Hello ' + a;
}
return b;
}
myFavoriteFunc('World');
console.log(a); // Throws a ReferenceError 'a' is not defined
console.log(b); // does not continue here
块作用域-在块{}
中声明的变量(let,const
)只能在其中访问。
function testBlock(){
if(true){
let z = 5;
}
return z;
}
testBlock(); // Throws a ReferenceError 'z' is not defined
/* 作用域链
内部作用域->外部作用域-> 全局作用域
*/
// 全局作用域
var variable1 = 'Comrades';
var variable2 = 'Sayonara';
function outer(){
// 外部作用域
var variable1 = 'World';
function inner(){
// 内部作用域
var variable2 = 'Hello';
console.log(variable2 + ' ' + variable1);
}
inner();
}
outer(); // Hello World
// 全局作用域
var globalVar = 'abc';
function a(){
console.log(globalVar);
}
a(); // 'abc'
a
函数时,全局作用域是a
闭包的一部分。globalVar
在图中没有值的原因是该变量的值可以根据调用函数a
的位置和时间而改变。但是在上面的示例中,globalVar
变量的值为abc
。var globalVar = 'global';
var outerVar = 'outer'
function outerFunc(outerParam) {
function innerFunc(innerParam) {
console.log(globalVar, outerParam, innerParam);
}
return innerFunc;
}
const x = outerFunc(outerVar);
outerVar = 'outer-2';
globalVar = 'guess'
x('inner');
guess outer inner
。outerFunc
函数并将返回值innerFunc
函数分配给变量x
时,即使我们为outerVar
变量分配了新值outer-2
,outerParam
也继续保留outer
值,因为重新分配是在调用outerFunc
之后发生的,并且当我们调用outerFunc
函数时,它会在作用域链中查找outerVar
的值,此时的outerVar
的值将为 'outer'
。innerFunc
的x
变量时,innerParam
将具有一个inner
值,因为这是我们在调用中传递的值,而globalVar
变量值为guess
,因为在调用x
变量之前,我们将一个新值分配给globalVar
。const arrFuncs = [];
for(var i = 0; i < 5; i++){
arrFuncs.push(function (){
return i;
});
}
console.log(i); // i is 5
for (let i = 0; i < arrFuncs.length; i++) {
console.log(arrFuncs[i]()); // 都打印 5
}
var
关键字创建一个全局变量,当我们 push 一个函数时,这里返回的全局变量i
。因此,当我们在循环后在该数组中调用其中一个函数时,它会打印5
,因为我们得到i
的当前值为5
,我们可以访问它,因为它是全局变量。let
来代替 var
的声明。const falsyValues = ['', 0, null, undefined, NaN, false];
false
的值。Boolean
函数或者 !!
运算符。'use strict'
是 ES5 特性,它使我们的代码在函数或整个脚本中处于严格模式。严格模式帮助我们在代码的早期避免 bug,并为其添加限制。变量必须声明后再使用
函数的参数不能有同名属性,否则报错
不能使用with
语句
不能对只读属性赋值,否则报错
不能使用前缀 0 表示八进制数,否则报错
不能删除不可删除的属性,否则报错
不能删除变量delete prop
,会报错,只能删除属性delete global[prop]
eval
不能在它的外层作用域引入变量
eval
和arguments
不能被重新赋值
arguments
不会自动反映函数参数的变化
不能使用arguments.callee
不能使用arguments.caller
禁止this
指向全局对象
不能使用fn.caller
和fn.arguments
获取函数调用的堆栈
增加了保留字(比如protected
、static
和interface
)
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
this
值是什么?this
指的是当前正在执行或调用该函数的对象的值。this
值的变化取决于我们使用它的上下文和我们在哪里使用它。const carDetails = {
name: 'Ford Mustang',
yearBought: 2005,
getName(){
return this.name;
},
isRegistered: true
};
console.log(carDetails.getName()); // Ford Mustang
getName
方法中我们返回this.name
,在此上下文中,this
指向的是carDetails
对象,该对象当前是执行函数的“所有者”对象。var name = 'Ford Ranger';
var getCarName = carDetails.getName;
console.log(getCarName()); // Ford Ranger
Ford Ranger
,这很奇怪,因为在第一个console.log
语句中打印的是Ford Mustang
。这样做的原因是getCarName
方法有一个不同的“所有者”对象,即window
对象。在全局作用域中使用var
关键字声明变量会在window
对象中附加与变量名称相同的属性。请记住,当没有使用“use strict”
时,在全局作用域中this
指的是window
对象。console.log(getCarName === window.getCarName); // true
console.log(getCarName === this.getCarName); // true
this
和window
引用同一个对象。apply
和call
方法。console.log(getCarName.apply(carDetails)); // Ford Mustang
console.log(getCarName.call(carDetails)); // Ford Mustang
apply
和call
方法期望第一个参数是一个对象,该对象是函数内部this
的值。IIFE
或立即执行的函数表达式,在全局作用域内声明的函数,对象内部方法中的匿名函数和内部函数的this
具有默认值,该值指向window
对象。(function (){
console.log(this);
})(); // 打印 'window' 对象
function iHateThis(){
console.log(this);
}
iHateThis(); // 打印 'window' 对象
const myFavoriteObj = {
guessThis(){
function getName(){
console.log(this.name);
}
getName();
},
name: 'Marko Polo',
thisIsAnnoying(callback){
callback();
}
};
myFavoriteObj.guessThis(); // 打印 'window' 对象
myFavoriteObj.thisIsAnnoying(function (){
console.log(this); // 打印 'window' 对象
});
myFavoriteObj
对象中的name
属性(即Marko Polo)的值,则有两种方法可以解决此问题。this
值保存在变量中。const myFavoriteObj = {
guessThis(){
const self = this; // 把 this 值保存在 self 变量中
function getName(){
console.log(self.name);
}
getName();
},
name: 'Marko Polo',
thisIsAnnoying(callback){
callback();
}
};
const myFavoriteObj = {
guessThis(){
const getName = () => {
//copies the value of 'this' outside of this arrow function
console.log(this.name);
}
getName();
},
name: 'Marko Polo',
thisIsAnnoying(callback){
callback();
}
};
this
。它复制了这个封闭的词法作用域中this
值,在这个例子中,this
值在getName
内部函数之外,也就是myFavoriteObj
对象。const o = {};
console.log(o.toString()); // logs [object Object]
o
对象中不存在o.toString
方法,它也不会引发错误,而是返回字符串[object Object]
。当对象中不存在属性时,它将查看其原型,如果仍然不存在,则将其查找到原型的原型,依此类推,直到在原型链中找到具有相同属性的属性为止。原型链的末尾是Object.prototype
。console.log(o.toString === Object.prototype.toString); // logs true
// which means we we're looking up the Prototype Chain and it reached
// the Object.prototype and used the 'toString' method.
function (){}
包裹在在括号()
内,然后再用另一个括号()
调用它,如:(function(){})()
(function(){
...
} ());
(function () {
...
})();
(function named(params) {
...
})();
(() => {
});
(function (global) {
...
})(window);
const utility = (function () {
return {
...
}
})
IIFE
的结果保存到变量中,以便稍后使用。<script src='https:///somelibrary.js'></script>
omelibr.js
的链接,它提供了一些我们在代码中使用的全局函数,但是这个库有两个方法我们没有使用:createGraph
和drawGraph
,因为这些方法都有bug
。我们想实现自己的createGraph
和drawGraph
方法。<script src='https:///somelibrary.js'></script>
<script>
function createGraph() {
// createGraph logic here
}
function drawGraph() {
// drawGraph logic here
}
</script>
<script src='https:///somelibrary.js'></script>
<script>
function myCreateGraph() {
// createGraph logic here
}
function myDrawGraph() {
// drawGraph logic here
}
</script>
<script src='https:///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
的对象。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
的值。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);
}
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}
/>
</>
)
}
}
function higherOrderFunction(param,callback){
return callback(param);
}
(var func = function(){})
、传参(function func(x,callback){callback();})
、返回(function(){return function(){}})
,这样的函数也称之为第一级函数(First-class Function)。不仅如此,JavaScript中的函数还充当了类的构造函数的作用,同时又是一个Function
类的实例(instance)。这样的多重身份让JavaScript的函数变得非常重要。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();
箭头函数
类
模板字符串
加强的对象字面量
对象解构
Promise
生成器
模块
Symbol
代理
Set
函数默认参数
rest 和展开
块作用域
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;
}
/*
* 1、一旦声明必须赋值,不能使用null占位。
*
* 2、声明后不能再修改
*
* 3、如果声明的是复合类型数据,可以修改其属性
*
* */
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();
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
//ES5 Version
var greet = 'Hi I\'m Mark';
//ES6 Version
let greet = `Hi I'm Mark`;
//ES5 Version
var lastWords = '\n'
+ ' I \n'
+ ' Am \n'
+ 'Iron Man \n';
//ES6 Version
let lastWords = `
I
Am
Iron Man
`;
\n
以在字符串中添加新行。在模板字符串中,我们不需要这样做。//ES5 Version
function greet(name) {
return 'Hello ' + name + '!';
}
//ES6 Version
function greet(name) {
return `Hello ${name} !`;
}
+
运算符。在模板字符串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;
CommonJS-Node.js
AMD(异步模块定义)-浏览器
import
用于从另一个文件中获取功能或几个功能或值,同时export
用于从文件中公开功能或几个功能或值。// 使用 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 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) - 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 - 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) - index.js
const Helpers = require('./helpers.js');
console.log(Helpers.isNull(null));
import Helpers from '.helpers.js'
console.log(Helpers.isNull(null));
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]
const btnAdd = document.getElementById('btnAdd');
btnAdd.addEventListener('click', function clickCallback(e) {
// do something useless
});
id
为btnAdd
的元素中的click
事件,如果它被单击,则执行clickCallback
函数。回调函数向某些数据或事件添加一些功能。reduce
、filter
和map
方法需要一个回调作为参数。回调的一个很好的类比是,当你打电话给某人,如果他们不接,你留下一条消息,你期待他们回调。调用某人或留下消息的行为是事件或数据,回调是你希望稍后发生的操作。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:初始状态,完成或失败状态的前一个状态
fulfilled:操作成功完成
rejected:操作失败
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) => {...});
async/await
及其如何工作?async/await
是 JS 中编写异步或非阻塞代码的新方法。它建立在Promises之上,让异步代码的可读性和简洁度都更高。async/await
是 JS 中编写异步或非阻塞代码的新方法。它建立在Promises
之上,相对于 Promise 和回调,它的可读性和简洁度都更高。但是,在使用此功能之前,我们必须先学习Promises
的基础知识,因为正如我之前所说,它是基于Promise
构建的,这意味着幕后使用仍然是Promise。function callApi() {
return fetch('url/to/api/endpoint')
.then(resp => resp.json())
.then(data => {
//do something with 'data'
}).catch(err => {
//do something with 'err'
});
}
async/await
,我们使用 tru/catch 语法来捕获异常。async function callApi() {
try {
const resp = await fetch('url/to/api/endpoint');
const data = await resp.json();
//do something with 'data'
} catch (e) {
//do something with 'err'
}
}
async
关键声明函数会隐式返回一个Promise。const giveMeOne = async () => 1;
giveMeOne()
.then((num) => {
console.log(num); // logs 1
});
await
关键字只能在async function
中使用。在任何非async function的函数中使用await
关键字都会抛出错误。await
关键字在执行下一行代码之前等待右侧表达式(可能是一个Promise)返回。const giveMeOne = async () => 1;
function getOne() {
try {
const num = await giveMeOne();
console.log(num);
} catch (e) {
console.log(e);
}
}
// Uncaught SyntaxError: await is only valid in async function
async function getTwo() {
try {
const num1 = await giveMeOne(); // 这行会等待右侧表达式执行完成
const num2 = await giveMeOne();
return num1 + num2;
} catch (e) {
console.log(e);
}
}
await getTwo(); // 2
...
),可以将一个数组转为用逗号分隔的参数序列。说的通俗易懂点,有点像化骨绵掌,把一个大元素给打散成一个个单独的小元素。...
)表示,它的样子看起来和展开操作符一样,但是它是用于解构数组和对象。在某种程度上,剩余元素和展开元素相反,展开元素会“展开”数组变成多个元素,剩余元素会收集多个元素和“压缩”成一个单一的元素。function add(a, b) {
return a + b;
};
const nums = [5, 6];
const sum = add(...nums);
console.log(sum);
add
函数时使用了展开操作符,对nums
数组进行展开。所以参数a
的值是5
,b
的值是6
,所以sum
是11
。function add(...rest) {
return rest.reduce((total,current) => total + current);
};
console.log(add(1, 2)); // 3
console.log(add(1, 2, 3, 4, 5)); // 15
add
函数,它接受任意数量的参数,并将它们全部相加,然后返回总数。const [first, ...others] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(others); // [2,3,4,5]
//ES5 Version
function add(a,b){
a = a || 0;
b = b || 0;
return a + b;
}
//ES6 Version
function add(a = 0, b = 0){
return a + b;
}
add(1); // returns 1
function getFirst([first, ...rest] = [0, 1]) {
return first;
}
getFirst(); // 0
getFirst([10,20,30]); // 10
function getArr({ nums } = { nums: [1, 2, 3, 4] }){
return nums;
}
getArr(); // [1, 2, 3, 4]
getArr({nums:[5,4,3,2,1]}); // [5,4,3,2,1]
function doSomethingWithValue(value = 'Hello World', callback = () => { console.log(value) }) {
callback();
}
doSomethingWithValue(); //'Hello World'
Undefined
,Null
,Boolean
,Number
,String
,Symbol
,BigInt
Object
,Array
,Date
,RegExp
等,说白了就是对象。let name = 'marko';
console.log(typeof name); // 'string'
console.log(name.toUpperCase()); // 'MARKO'
name
类型是 string
,属于基本类型,所以它没有属性和方法,但是在这个例子中,我们调用了一个toUpperCase()
方法,它不会抛出错误,还返回了对象的变量值。name
变量的行为类似于对象。除null
和undefined
之外的每个基本类型都有自己包装对象。也就是:String
,Number
,Boolean
,Symbol
和BigInt
。在这种情况下,name.toUpperCase()
在幕后看起来如下:console.log(new String(name).toUpperCase()); // 'MARKO'
console.log(1 + '6'); // 16
console.log(false + true); // 1
console.log(6 * '2'); // 12
console.log
语句结果为16
。在其他语言中,这会抛出编译时错误,但在 JS 中,1
被转换成字符串,然后与+运
算符连接。我们没有做任何事情,它是由 JS 自动完成。console.log
语句结果为1
,JS 将false
转换为boolean
值为 0
,,true
为1
,因此结果为1
。console.log
语句结果12
,它将'2'
转换为一个数字,然后乘以6 * 2
,结果是12。console.log(1 + parseInt('6'));
parseInt
函数将'6'
转换为number
,然后使用+
运算符将1
和6
相加。NaN
表示“非数字”是 JS 中的一个值,该值是将数字转换或执行为非数字值的运算结果,因此结果为NaN
。let a;
console.log(parseInt('abc')); // NaN
console.log(parseInt(null)); // NaN
console.log(parseInt(undefined)); // NaN
console.log(parseInt(++a)); // NaN
console.log(parseInt({} * 10)); // NaN
console.log(parseInt('abc' - 2)); // NaN
console.log(parseInt(0 / 0)); // NaN
console.log(parseInt('10a' * 10)); // NaN
isNaN
方法,用于测试值是否为isNaN值,但是这个函数有一个奇怪的行为。console.log(isNaN()); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
console.log(isNaN(String('a'))); // true
console.log(isNaN(() => { })); // true
console.log
语句都返回true
,即使我们传递的值不是NaN
。ES6
中,建议使用Number.isNaN
方法,因为它确实会检查该值(如果确实是NaN
),或者我们可以使自己的辅助函数检查此问题,因为在 JS 中,NaN是唯一的值,它不等于自己。function checkIfNaN(value) {
return value !== value;
}
Array.isArray
方法来检查值是否为数组。当传递给它的参数是数组时,它返回true
,否则返回false
。console.log(Array.isArray(5)); // false
console.log(Array.isArray('')); // false
console.log(Array.isArray()); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray({ length: 5 })); // false
console.log(Array.isArray([])); // true
polyfill
实现。function isArray(value){
return Object.prototype.toString.call(value) === '[object Array]'
}
let a = []
if (a instanceof Array) {
console.log('是数组')
} else {
console.log('非数组')
}
%
模运算符的情况下检查一个数字是否是偶数?&
运算符,&
对其操作数进行运算,并将其视为二进制值,然后执行与运算。function isEven(num) {
if (num & 1) {
return false
} else {
return true
}
}
0
二进制数是 000
1
二进制数是 001
2
二进制数是 010
3
二进制数是 011
4
二进制数是 100
5
二进制数是 101
6
二进制数是 110
7
二进制数是 111
console.log(5&1)
这个表达式时,结果为1
。首先,&
运算符将两个数字都转换为二进制,因此5
变为101
,1
变为001
。0
和1
)。 101&001
,从表中可以看出,如果a & b
为1
,所以5&1
结果为1
。首先我们比较最左边的1&0
,结果是0
。
然后我们比较中间的0&0
,结果是0
。
然后我们比较最后1&1
,结果是1
。
最后,得到一个二进制数001
,对应的十进制数,即1
。
console.log(4 & 1)
结果为0
。知道4
的最后一位是0
,而0 & 1
将是0
。如果你很难理解这一点,我们可以使用递归函数来解决此问题。function isEven(num) {
if (num < 0 || num === 1) return false;
if (num == 0) return true;
return isEven(num - 2);
}
in
操作符号:const o = {
'prop' : 'bwahahah',
'prop2' : 'hweasa'
};
console.log('prop' in o); // true
console.log('prop1' in o); // false
hasOwnProperty
方法,hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。console.log(o.hasOwnProperty('prop2')); // true
console.log(o.hasOwnProperty('prop1')); // false
obj['prop']
。如果属性存在,它将返回该属性的值,否则将返回undefined
。console.log(o['prop']); // 'bwahahah'
console.log(o['prop1']); // undefined
HTML - 网页结构
CSS - 网页的样式
JavaScript - 操作网页的行为和更新DOM
XMLHttpRequest API - 用于从服务器发送和获取数据
PHP,Python,Nodejs - 某些服务器端语言
const o = {
name: 'Mark',
greeting() {
return `Hi, I'm ${this.name}`;
}
};
o.greeting(); //returns 'Hi, I'm Mark'
function Person(name) {
this.name = name;
}
Person.prototype.greeting = function () {
return `Hi, I'm ${this.name}`;
}
const mark = new Person('Mark');
mark.greeting(); //returns 'Hi, I'm Mark'
const n = {
greeting() {
return `Hi, I'm ${this.name}`;
}
};
const o = Object.create(n); // sets the prototype of 'o' to be 'n'
o.name = 'Mark';
console.log(o.greeting()); // logs 'Hi, I'm Mark'
Object.freeze()
方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze()
返回和传入的参数相同的对象。ES5新增。
对象不可能扩展,也就是不能再添加新的属性或者方法。
对象已有属性不允许被删除。
对象属性特性不可以重新配置。
Object.seal
方法生成的密封对象,如果属性是可写的,那么可以修改属性值。
Object.freeze
方法生成的冻结对象,属性都是不可写的,也就是属性值无法更改。
in
运算符和 Object.hasOwnProperty
方法有什么区别?hasOwnPropert()
方法返回值是一个布尔值,指示对象自身属性中是否具有指定的属性,因此这个方法会忽略掉那些从原型链上继承到的属性。Object.prototype.phone= '15345025546';
let obj = {
name: '西门大官人',
age: '28'
}
console.log(obj.hasOwnProperty('phone')) // false
console.log(obj.hasOwnProperty('name')) // true
phone
,hasOwnProperty
方法会直接忽略掉。in
运算符返回true
。console.log('phone' in obj) // true
in
运算符会检查它或者其原型链是否包含具有指定名称的属性。回调
Promise
async/await
还有一些库:async.js, bluebird, q, co
hoistedFunc();
notHoistedFunc();
function hoistedFunc(){
console.log('注意:我会被提升');
}
var notHoistedFunc = function(){
console.log('注意:我没有被提升');
}
notHoistedFunc
调用抛出异常:Uncaught TypeError: notHoistedFunc is not a function
,而hoistedFunc
调用不会,因为hoistedFunc
会被提升到作用域的顶部,而notHoistedFunc
不会。作为函数调用——如果一个函数没有作为方法、构造函数、apply
、call
调用时,此时 this
指向的是 window
对象(非严格模式)
//Global Scope
function add(a,b){
console.log(this);
return a + b;
}
add(1,5); // 打印 'window' 对象和 6
const o = {
method(callback){
callback();
}
}
o.method(function (){
console.log(this); // 打印 'window' 对象
});
作为方法调用——如果一个对象的属性有一个函数的值,我们就称它为方法。调用该方法时,该方法的this
值指向该对象。
const details = {
name : 'Marko',
getName(){
return this.name;
}
}
details.getName(); // Marko
// the 'this' value inside 'getName' method will be the 'details' object
作为构造函数的调用-如果在函数之前使用new
关键字调用了函数,则该函数称为构造函数
。构造函数里面会默认创建一个空对象,并将this
指向该对象。
function Employee(name, position, yearHired) {
// creates an empty object {}
// then assigns the empty object to the 'this' keyword
// this = {};
this.name = name;
this.position = position;
this.yearHired = yearHired;
// inherits from Employee.prototype
// returns the 'this' value implicitly if no
// explicit return statement is specified
};
const emp = new Employee('Marko Polo', 'Software Developer', 2017);
使用apply
和call
方法调用——如果我们想显式地指定一个函数的this
值,我们可以使用这些方法,这些方法对所有函数都可用。
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]);
// reduceAdd 函数中的 this 对象将是 obj1
reduceAdd.call(obj2, 1, 2, 3, 4, 5);
// reduceAdd 函数中的 this 对象将是 obj2
function memoize(fn) {
const cache = {};
return function (param) {
if (cache[param]) {
console.log('cached');
return cache[param];
} else {
let result = fn(param);
cache[param] = result;
console.log(`not cached`);
return result;
}
}
}
const toUpper = (str ='')=> str.toUpperCase();
const toUpperMemoized = memoize(toUpper);
toUpperMemoized('abcdef');
toUpperMemoized('abcdef');
const slice = Array.prototype.slice;
function memoize(fn) {
const cache = {};
return (...args) => {
const params = slice.call(args);
console.log(params);
if (cache[params]) {
console.log('cached');
return cache[params];
} else {
let result = fn(...args);
cache[params] = result;
console.log(`not cached`);
return result;
}
}
}
const makeFullName = (fName, lName) => `${fName} ${lName}`;
const reduceAdd = (numbers, startingValue = 0) =>
numbers.reduce((total, cur) => total + cur, startingValue);
const memoizedMakeFullName = memoize(makeFullName);
const memoizedReduceAdd = memoize(reduceAdd);
memoizedMakeFullName('Marko', 'Polo');
memoizedMakeFullName('Marko', 'Polo');
memoizedReduceAdd([1, 2, 3, 4, 5], 5);
memoizedReduceAdd([1, 2, 3, 4, 5], 5);
typeof null == 'object'
总是返回true
,因为这是自 JS 诞生以来null
的实现。曾经有人提出将typeof null == 'object'
修改为typeof null == 'null'
,但是被拒绝了,因为这将导致更多的bug。===
来检查值是否为null
。function isNull(value){
return value === null;
}
new
关键字与构造函数一起使用以创建对象在JavaScript中。function Employee(name, position, yearHired) {
this.name = name;
this.position = position;
this.yearHired = yearHired;
};
const emp = new Employee('Marko Polo', 'Software Developer', 2017);
new
关键字做了4
件事:创建空对象 {}
将空对象分配给 this
值
将空对象的__proto__
指向构造函数的prototype
如果没有使用显式return
语句,则返回this
|