知识传递之旅:探索JavaScript易混淆问题二

"困惑与问题若不及时解决,将成为我们前进道路上的拦路虎,让我们畏首畏尾,迟疑不前。"

简介:

本篇文章主要介绍了 JavaScript 中的 ES5ES6 的区别,callapplybind绑定的区别,this 关键字的用法和特性。通过讲解隐式类型转换、显式类型转换以及绑定函数、导入导出等,帮助读者了解 JavaScript 中的一些常见问题,避免在代码编写中出现意外的结果。

第二篇:进阶篇

未来会持续性更新内容😘。

可以回顾一下第一篇: # 知识传递之旅:探索JavaScript易混淆问题一

ES5与ES6的导入导出

ES5 的模块导入和导出

在 ES5 中,没有原生的模块系统,通常使用 CommonJS 规范来实现模块的导入和导出。

导出模块:

使用 module.exportsexports 将模块中的内容导出。

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 中,引入了原生的模块系统,使用 importexport 来实现模块的导入和导出。

导出模块:

使用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 绑定

callbindapply 是 JavaScript 中用于显式绑定函数执行上下文的方法。它们的主要作用是改变函数中的 this 指向,以及传递参数

callbindapply 方法都可以用来改变函数执行上下文,但是它们并不会修改原函数的 this 指向,而是返回一个新的函数或立即执行原函数

call、apply 区别

callapply 方法的主要区别在于传递参数的方式:call 方法接收单独的参数列表,而 apply 方法接收一个数组或类数组对象。

使用 callapply 方法指定执行上下文并调用函数(立即执行原函数)

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!!!"

总结

  1. callbindapply 方法的传递参数区别:callbind 方法接收单独的参数列表 ,而 apply 方法接收一个数组或类数组对象

  2. callapplybind 方法的执行函数区别:callapply会立即执行函数,bind 方法用于创建一个新的函数,而不会立即执行原函数。新函数可以稍后调用,并且在调用时会使用指定的上下文。

  3. 如果不需要指定执行上下文,可以将第一个参数传递为 nullundefined,此时上下文将是全局对象(在浏览器中是 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 }

解析:

  1. bar 函数在全局环境中被调用时,this 指向全局对象 window
  2. 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] }

解析:

  1. obj1 中的 foo 方法只是定义了一个函数,但没有被调用。
  2. obj2 中的 bar 属性被赋值为 obj1.foo 方法的引用,而不是调用 obj1.foo()
  3. obj2.bar() 调用中,bar 方法在 obj2 上下文中被调用,所以 this 指向 obj2 对象。

案例三:显示绑定

上面提到的 callapplybind 的绑定。

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 绑定到该对象上。

相关推荐
stayong13 分钟前
市面主流跨端开发框架对比
前端
一米八二的矮个子17 分钟前
JavaScript语法进阶(一)
javascript
庞囧26 分钟前
大白话讲 React 原理:Scheduler 任务调度器
前端
liyi_hz200827 分钟前
O2OA (翱途)开发平台新版本发布预告:架构升级、性能跃迁、功能全面进化
android·java·javascript·开源软件
东华帝君39 分钟前
react 虚拟滚动列表的实现 —— 动态高度
前端
CptW41 分钟前
手撕 Promise 一文搞定
前端·面试
温宇飞42 分钟前
Web 异步编程
前端
腹黑天蝎座42 分钟前
浅谈React19的破坏性更新
前端·react.js
东华帝君42 分钟前
react组件常见的性能优化
前端
第七种黄昏42 分钟前
【前端高频面试题】深入浏览器渲染原理:从输入 URL 到页面绘制的完整流程解析
前端·面试·职场和发展