"困惑与问题若不及时解决,将成为我们前进道路上的拦路虎,让我们畏首畏尾,迟疑不前。"
简介:
本篇文章主要介绍了 JavaScript 中的
ES5
与ES6
的区别,call
、apply
、bind
绑定的区别,this
关键字的用法和特性。通过讲解隐式类型转换、显式类型转换以及绑定函数、导入导出等,帮助读者了解 JavaScript 中的一些常见问题,避免在代码编写中出现意外的结果。
第二篇:进阶篇
未来会持续性更新内容😘。
可以回顾一下第一篇: # 知识传递之旅:探索JavaScript易混淆问题一
ES5与ES6的导入导出
ES5 的模块导入和导出
在 ES5 中,没有原生的模块系统,通常使用 CommonJS 规范来实现模块的导入和导出。
导出模块:
使用 module.exports
或 exports
将模块中的内容导出。
js
// moduleA.js
function add(a, b) {
return a + b;
}
module.exports = {
add: add,
someValue: 66
};
导入模块:
使用 require()
函数来导入模块中的内容。
js
// main.js
var moduleA = require('./moduleA');
console.log(moduleA.add(2, 3)); // 输出 5
console.log(moduleA.someValue); // 输出 66
ES6 的模块导入和导出
在 ES6 中,引入了原生的模块系统,使用 import
和 export
来实现模块的导入和导出。
导出模块:
使用export
关键字将模块中的内容导出。
js
// moduleB.js
export function subtract(a, b) {
return a - b;
}
export const anotherValue = 100;
导入模块:
使用 import
关键字来导入模块中的内容。
js
// main.js
import { subtract, anotherValue } from './moduleB';
console.log(subtract(5, 3)); // 输出 2
console.log(anotherValue); // 输出 100
ES6 模块导出的其他形式
ES6 中,还支持默认导出和默认导入的方式。
默认导出:
使用 export default
关键字来导出一个模块的默认内容。
js
// moduleC.js
const greeting = "Hello, Maotou!";
export default greeting;
默认导入:
在导入时,可以使用 import
关键字后直接跟变量名,表示默认导入模块的默认内容。
js
// main.js
import greeting from './moduleC';
console.log(greeting); // 输出 "Hello, Maotou!"
ES5 与 ES6 总结
"N/A" 是 "Not Applicable"(不适用)的缩写
特性 | ES5 | ES6 |
---|---|---|
导出模块 | module.exports / exports | export |
导出单个内容 | N/A | export default |
导入模块 | require() | import |
导入具名内容 | N/A | import { ... } |
导入默认内容 | N/A | import ... from ... |
导入重命名内容 | N/A | import { ... as ... } |
顶层作用域 | 是 | 是 |
动态模块路径 | 是 | 否 |
支持静态解析 | 否 | 是 |
需要构建工具转换 | 否 | 是 |
call、apply、bind 绑定
call
、bind
和 apply
是 JavaScript 中用于显式绑定函数执行上下文的方法。它们的主要作用是改变函数中的 this
指向,以及传递参数
call
、bind
和 apply
方法都可以用来改变函数执行上下文,但是它们并不会修改原函数的 this
指向,而是返回一个新的函数或立即执行原函数
call、apply 区别
call
和 apply
方法的主要区别在于传递参数的方式:call
方法接收单独的参数列表,而 apply
方法接收一个数组或类数组对象。
使用 call
或 apply
方法指定执行上下文并调用函数(立即执行原函数)
js
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: "Maotou" };
// 使用 call 方法,参数一个个地列出
greet.call(person, "Hello", "!"); // 输出 "Hello, Maotou!"
// 使用 apply 方法,参数作为数组传递
const args = ["Hi", "!!!"];
greet.apply(person, args); // 输出 "Hi, Maotou!!!"
ES6,中有了新的写法
js
// 等效于 greet.apply(person, args);
greet.call(person, ...args);
这样我们,就需要避免使用 apply
方法,使用扩展运算符 ...
与call
的结合,这样代码更简洁,易读性更高,
bind 与 call、apply 区别
bind
方法用于创建一个新的函数 ,并指定新函数执行时的上下文对象。它不会立即执行函数,而是返回一个绑定了指定上下文的新函数,并且新函数可以稍后调用。
bind
方法还可以传递参数,这些参数会作为新函数的前置参数
js
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: "Maotou" };
// 使用 bind 方法创建新函数,并绑定上下文和多个参数
const greetPerson = greet.bind(person, "Hello");
// 调用新函数,传递额外的参数
greetPerson(); // 输出 "Hello, Maotou"
// 也可以传递更多参数
const greetPersonExclaim = greet.bind(person, "Hello", "!!!");
greetPersonExclaim(); // 输出 "Hello, Maotou!!!"
总结
-
call
、bind
和apply
方法的传递参数区别:call
、bind
方法接收单独的参数列表 ,而apply
方法接收一个数组或类数组对象。 -
call
、apply
和bind
方法的执行函数区别:call
和apply
会立即执行函数,bind
方法用于创建一个新的函数,而不会立即执行原函数。新函数可以稍后调用,并且在调用时会使用指定的上下文。 -
如果不需要指定执行上下文,可以将第一个参数传递为
null
或undefined
,此时上下文将是全局对象(在浏览器中是window
,在 Node.js 中是global
)。
this 关键字
基本谁调用,就指向谁,找准到底是谁在调用。
全局上下文中的 this
在全局上下文中,this
指向全局对象,在浏览器中通常是 window
对象,在 Node.js 中是 global
对象。
js
console.log(this === window); // 输出 true,在浏览器中
console.log(this === global); // 输出 true,在 Node.js 中
函数中的 this
:
在函数中,this
的值取决于函数的调用方式。在普通函数中,this
可能指向全局对象或 undefined
,取决于是否在严格模式下
js
function greet() {
console.log(this === window); // 输出 true,普通函数中的 this 指向全局对象
}
greet();
function strictGreet() {
"use strict";
console.log(this === undefined); // 输出 true,在严格模式下,普通函数中的 this 指向 undefined
}
strictGreet();
对象方法中的 this
:
在对象方法中,this
指向调用该方法的对象。
js
const person = {
name: "Maotou",
greet: function() {
console.log(this.name); // 输出 "Maotou",对象方法中的 this 指向 person 对象
}
};
person.greet();
构造函数中的 this
:
在构造函数中,this
指向新创建的对象。
js
function Person(name) {
this.name = name;
}
const maotou = new Person("Maotou");
console.log(maotou.name); // 输出 "Maotou"
箭头函数中的 this
:
箭头函数中的 this
指向外层(定义箭头函数的函数或全局)函数的 this
值,或者如果箭头函数没有外层函数,则指向全局对象。
js
const person = {
name: "Maotou",
greet: function() {
const arrowGreet = () => {
console.log(this.name); // 输出 "Maotou",箭头函数中的 this 指向外层函数的 this 值
};
arrowGreet();
}
};
person.greet();
箭头函数的 this
取决于外层函数的 this
。
对于箭头函数的使用需要小心
案例一:默认绑定
独立的函数调用,我们可以理解成函数没有被绑定到某个对象上进行调用
js
function foo(){
function bar(){
console.log(this) //this 是window
}
return bar
}
var fn = foo()
fn() // 打印全局window
var obj = {
name:"Maotou",
eating:fn
}
obj.eating() // 打印{ name: "Maotou", eating: fn }
解析:
bar
函数在全局环境中被调用时,this
指向全局对象window
。obj.eating()
调用中,eating
函数的执行上下文被绑定到obj
对象,所以this
指向obj
对象。因此,在bar
函数中打印this
,将输出obj
对象的内容{ name: "Maotou", eating: fn }
案例二:隐式调用
它的调用,是通过某个对象发起的函数调用
js
var obj1 = {
name:'obj1',
foo:function() {
console.log(this);
}
}
var obj2 = {
name:"obj2",
bar:obj1.foo //obj1没有调用foo 只是调了一个引用地址
}
obj2.bar(); // { name: 'obj2', bar: [Function: foo] }
解析:
obj1
中的foo
方法只是定义了一个函数,但没有被调用。obj2
中的bar
属性被赋值为obj1.foo
方法的引用,而不是调用obj1.foo()
。obj2.bar()
调用中,bar
方法在obj2
上下文中被调用,所以this
指向obj2
对象。
案例三:显示绑定
上面提到的 call
、apply
、bind
的绑定。
js
function foo(){
console.log(this)
}
var obj = {
name: "Maotou"
}
var bar = foo.bind(obj);
obj() // obj
解析:
现在,当我们调用 bar()
时,函数 foo
的执行上下文会被绑定为 obj
,所以 this
指向了 obj
对象,输出 { name: "Maotou" }
。这是因为我们使用了 bind
方法来显式绑定了 foo
函数的执行上下文。
案例四:new绑定
类的构造函数,使用new关键字来调用函数是
js
function Person(name,age){
console.log(this) // Person {}
this.name = name; // Person {name:"Maotou"}
}
var p =new Person("Maotou");
console.log(p.name); // Maotou
解析:
通过构造函数创建了实例,并且每个实例都拥有自己的属性值。构造函数在每次被调用时,都会创建一个新的对象,并将 this
绑定到该对象上。