知识传递之旅:探索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 绑定到该对象上。

相关推荐
YBN娜几秒前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=几秒前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck5 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!26 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。31 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼37 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k093341 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning1 小时前
React.lazy() 懒加载
前端·react.js·前端框架
时差9531 小时前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database