Let/Const

Var 在 ES6 出现之前,必须使用 var 声明。但是,前端开发出现的一些问题与使用 var 声明的变量有关。这就是为什么必须要有新的方法来声明变量。首先,让我们在讨论这些问题之前更多地了解 var。 var 的作用域 作用域本质上是指变量/函数可供访问的范围。var可以在全局范围声明或函数/局部范围内声明。 当在最外层函数的外部声明var变量时,作用域是全局的。这意味着在最外层函数的外部用var声明的任何变量都可以在windows中使用。 当在函数中声明var时,作用域是局部的。这意味着它只能在函数内访问。 要进一步了解,请查看下面的示例。 var greeter = 'hey hi'; function newFunction() { var hello = 'hello'; } 这里,greeter是全局范围的,因为它存在于函数外部,而hello是函数范围的。因此,我们无法在函数外部访问变量hello。因此,如果我们这样做: var tester = 'hey hi'; function newFunction() { var hello = 'hello'; } console.log(hello); // error: hello is not defined 我们会收到错误消息,这是由于函数外部没有hello导致的。 var 变量可以重新声明和修改 这意味着我们可以在相同的作用域内执行下面的操作,并且不会出错 var greeter = 'hey hi'; var greeter = 'say Hello instead'; 又比如 var greeter = 'hey hi'; greeter = 'say Hello instead'; var 的变量提升 变量提升是 JavaScript 的一种机制:在执行代码之前,变量和函数声明会移至其作用域的顶部。这意味着如果我们这样做: console.log(greeter); var greeter = 'say hello'; 生面的代码会被解释为: var greeter; console.log(greeter); // greeter is undefined greeter = 'say hello'; 因此,将var声明的变量会被提升到其作用域的顶部,并使用 undefined 值对其进行初始化. ...

October 2, 2024

模版字符串

未见过的函数用法: function fun(strings,params1){ console.log(strings); console.log(params1) } fun`params1 = ${333}` // 输出分别为 // ['params1 = ', ''] // 333 不知道各位在学习或开发中有没有遇到过这样的函数调用方式呢?秉承着我没遇到的东西就要发文章来分享分享的原则,经过我查阅资料并加以学习,就让本文来带大家了解一下JS中的标签函数吧。 模板字符串 标签函数的主要作用就是处理模板字符串,所以在了解标签函数之前先了解一下模板字符串(模板字面量)吧。 基本用法 模板字符串是ES6新增的使用模板字面量定义字符串的能力,用反引号```````` 进行分隔。与使用''或""的定义的字符串字面量不同是模板字面量保留换行字符,可以跨行定义字符串。 例如: let str1 = 'xxhh\n2233' let str2 = `xxhh 2233` console.log(str1 === str2) // 结果为true 要注意的是模板字符串中的所有字符都会包含在字符串中,特别在使用缩进的时候,缩进的空格都会包含在字符串中。 let str = ` <div> <h1>xxhh</h1> </div> ` console.log(str.length) // 结果为44,包含了缩进所产出的所有空格 字符串插值 在没有模板字符串时,我们通常会使用+加法运算符来进行字符串的拼接,但有了模板字符串后我们并可通过插值占位符${}来将各字面量、变量或表达式结果来进行拼接,这样可以更容易地创建复杂和动态的字符串,同时保持代码的可读性和简洁性。这是模板字符串最常用的一个特性。 let name = 'xxhh'; let str1 = "name:"+name; let str2 = `name:${name}`; console.log(str1,str2) // name:xxhh name:xxhh console.log(str1 === str2) // true 要注意的是使用字符串插值的所有插入的值都会使用toString()强制转型为字符串,而且任何JavaScript 表达式都可以用于插值。 ...

October 2, 2024

默认参数

一、参数传递 代码语言:javascript function foo(a, b) { console.log([a, b]); } foo(1, 2); // 输出 [1, 2] 这个例子中,a 和 b 属于函数中的局部变量,只能在函数中访问。调用函数时,传递的数据会根据位置来匹配对应,分别赋值给 a 和 b。 创建函数时,function 函数名 后面括号中设定的参数被称为形参;调用函数时,函数名后面括号中传入的参数被称为实参。上面例子中,a 和 b 是形参,传入的 1 和 2 是实参。 因为形参是已声明的变量,所以不能再用 let 和 const 重复声明。 代码语言:javascript function foo(a, b) { let a = 1; // 报错,a 已声明 const b = 1; // 报错,b 已声明 } JavaScript 中所有函数传递都是按值传递的,不会按引用传递。所谓的值,就是指直接保存在变量上的值,如果把对象作为参数传递,那么这个值就是这个对象的引用,而不是对象本身。这里实际上是一个隐式的赋值过程,所以给函数传递参数时,相当于从一个变量赋值到另一个变量。 原始值: 代码语言:javascript function add(num) { return num + 1; } let count = 5; let result = add(count); // 此处参数传递的过程可以看作是 num = count console.log(count); // 5 console.log(result); // 6 引用值: ...

October 2, 2024

箭头函数

一、箭头函数的特点 1. 相比普通函数,箭头函数有更加简洁的语法。 普通函数 function add(num) { return num + 10 } 箭头函数 const add = num => num + 10; 2. 箭头函数不绑定this,会捕获其所在上下文的this,作为自己的this。 这句话需要注意的是,箭头函数的外层如果有普通函数,那么箭头函数的this就是这个外层的普通函数的this,箭头函数的外层如果没有普通函数,那么箭头函数的this就是全局变量。 下面这个例子是箭头函数的外层有普通函数。 let obj = { fn:function(){ console.log('我是普通函数',this === obj) // true return ()=>{ console.log('我是箭头函数',this === obj) // true } } } console.log(obj.fn()()) 下面这个例子是箭头函数的外层没有普通函数。 let obj = { fn:()=>{ console.log(this === window); } } console.log(obj.fn()) // true 3. 箭头函数是匿名函数,不能作为构造函数,不可以使用new命令,否则后抛出错误。 ...

October 2, 2024

解构赋值

解构赋值概念 解构赋值语法是一种 Javascript 表达式。 通过解构赋值,可以将属性/值从对象/数组中取出,赋值给其他变量。 解构赋值是通过将结构中的各元素复制到变量中来达到“解构”的目的。但对象/数组本身不会被修改。 数组解构 基本使用 📖 可以在定义变量的同时,使用解构进行赋值 let [one, two, three] = foo; //等同于 let one = foo[0]; let two = foo[1]; let three = foo[2]; 📖 也可以先声明变量,在进行解构赋值 设置默认值 当【左边≠右边】时,可以先设置一个默认值,否则取不到值的变量会变成undefined 剩余值处理 可以使用三个点 "..."+ 参数来接收剩余的所有元素,变量名称随意,必须在最后一个参数的位置上 进阶用法 🎨 可以和split 函数结合使用,比如 let [Mother,Father] = "judy,pete".split(','); console.log(Mother) //judy console.log(Father) //pete 🎨 可以使用逗号忽略元素,比如 let [name1,,name3] = ["Julius01", "Caesar02", "Consul03"] 🎨 可以直接赋值给对象的属性,比如 let user = {}; [user.name, user.surname] = ["Ilya","Kantor"] ...

October 2, 2024

展开运算符

ES6 新增了很多特性,扩展运算符就是其中一个。简单的三个点号,让原本繁琐的代码瞬间变得简洁、优雅。就让我们看看它有哪些常见用法吧。 让函数的 Apply 调用方式成为过去 有时候我们需要通过 Function.prototype.apply 来调用函数,参数就放在一个数组里。 function doStuff (x, y, z) { } var args = [0, 1, 2]; // 调用函数并传参 doStuff.apply(null, args); 现在有了展开运算符,我们可以完全避免使用 apply,只需在调用函数时在数组前加上展开运算符: doStuff(...args); 代码变得更短、更简洁,也不需要一个毫无用处的 null! 合并数组 合并数组有各种方式,但是展开运算符为我们提供了一种新思路: arr1.push(...arr2) // 在数组后面添加 arr2 里的元素 arr1.unshift(...arr2) //在数组前面添加 arr2 里的元素 如果你想合并两个数组,并且想把元素放到数组中的任何位置,你可以这么做: var arr1 = ['two', 'three']; var arr2 = ['one', ...arr1, 'four', 'five']; // ["one", "two", "three", "four", "five"] 代码少了很多,还可以随意控制位置! 拷贝数组 拷贝数组是个常见的任务,过去我们使用 Array.prototype.slice 来完成,但现在我们可以通过展开运算符得到数组的副本: var arr = [1,2,3]; var arr2 = [...arr]; // 等同于 arr.slice() arr2.push(4) 这里要注意,数组里的对象仍然是引用类型的,因此实际上并不是所有内容都被“拷贝”了。 将参数或 NodeList 转成数组 跟拷贝数组类似,我们用 Array.Prototype.slice 将 NodeList和 arguments 对象转成真正的数组,但是现在我们可以使用展开运算符完成这个任务: ...

October 2, 2024

export/import

export,import 是什么 我们知道,JS 模块导入导出,使用 import , export 这两个关键字 export 用于对外输出本模块 import 用于导入模块 也就是说使用 export 导出一个模块之后,其它文件就可以使用 import 导入相应的模块了 下面我们具体看看, import 和 export 到底怎么用?怎么导出模块(比如变量,函数,类,对象等) 1 导出单个变量 //a.js 导出一个变量,语法如下 export var site = "www.helloworld.net" //b.js 中使用import 导入上面的变量 import { site } from "/.a.js" //路径根据你的实际情况填写 console.log(site) //输出: www.helloworld.net 2 导出多个变量 上面的例子是导出单个变量,那么如何导出多个变量呢 //a.js 中定义两个变量,并导出 var siteUrl="www.helloworld.net" var siteName="helloworld开发者社区" //将上面的变量导出 export { siteUrl ,siteName } // b.js 中使用这两个变量 import { siteUrl , siteName } from "/.a.js" //路径根据你的实际情况填写 console.log(siteUrl) //输出: www.helloworld.net console.log(siteName) //输出: helloworld开发者社区 ``` ### 3 导出函数 导出函数和导出变量一样,需要添加`{ }` ````js //a.js 中定义并导出一个函数 function sum(a, b) { return a + b } //将函数sum导出 export { sum } //b.js 中导入函数并使用 import { sum } from "/.a.js" //路径根据你的实际情况填写 console.log( sum(4,6) ) //输出: 10 ``` ### 4 导出对象 js中一切皆对象,所以对象一定是可以导出的,并且有两种写法 #### 4.1 第一种写法 使用 `export default` 关键字导出,如下 ````js //a.js 中,定义对象并导出, 注意,使用export default 这两个关键字导出一个对象 export default { siteUrl:'www.helloworld.net', siteName:'helloworld开发者社区' } //b.js 中导入并使用 import obj from './a.js' //路径根据你的实际情况填写 console.log(obj.siteUrl) //输出:www.helloworld.net console.log(obj.siteName) //输出:helloworld开发者社区 ``` #### 4.2 第二种写法 同样是使用`export default` 关键字,如下 ````js //a.js 中定义对象,并在最后导出 var obj = { siteUrl:'www.helloworld.net', siteName:'helloworld开发者社区' } export default obj //导出对象obj //b.js 中导入并使用 import obj from './a.js' //路径根据你的实际情况填写 console.log(obj.siteUrl) //输出:www.helloworld.net console.log(obj.siteName) //输出:helloworld开发者社区 ``` ### 5 导出类 导出类与上面的导出对象类似,同样是用 `export default` 关键字,同样有两种写法 #### 5.1 第一种写法 ````js //a.js 中定义一个类并直接导出 export default class Person { //类的属性 site = "www.helloworld.net" //类的方法 show(){ console.log(this.site) } } //b.js 中导入并使用 //导入类 import Person from './a.js' //创建类的一个对象person let person = new Person() //调用类的方法 person.show() //输出:www.helloworld.net ``` #### 5.2 第二种写法 ````js //a.js 中定义一个类,最后导出 class Person { //类的属性 site = "www.helloworld.net" //类的方法 show(){ console.log(this.site) } } //导出这个类 export default Person //b.js 中导入并使用 //导入类 import Person from './a.js' //创建类的一个对象person let person = new Person() //调用类的方法 person.show() //输出:www.helloworld.net ``` ### 小结 下面我们简单总结一下 `export`与`export default`的区别 * export与export default均可用于导出常量、函数、文件、模块等 * 可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用 * export default后面不能跟const或let的关键词 * export、import可以有多个,export default仅有一个。 * 通过export方式导出,在导入时要加 { },export default则不需要 * export具名导出xxx ,export default匿名。区别在于导入的时候,export需要一样的名称才能匹配,后者无论取什么名都可以。 * 模块化管理中一个文件就是一个模块,export可以导出多个方法和变量,export default只能导出当前模块,一个js文件中只支持出现一个 对于 `import` ,`export` , `export default` ,他们的用法上面的例子已经很详细的列出了,忘记的时候,可以当作参考看看 最重要的还是要明白为什么要这么写,实在不明白记住就行了。 常见考点 1. export 的基本使用 export 用于导出模块中的内容(如变量、函数、类等),使得其他模块能够导入这些内容。 ...

October 2, 2024

Promise

Promise 是在 ES6 出现的新功能,并且是用来优化过去回调函式 callback 的写法。这篇面试详解中会讨论到, promise 是什么、为什么需要有 Promise,以及延伸到 async/await 相关问题。 为什么要使用 Promise? 在探讨 Promise 之前,我们先来看一下为什么需要它的出现。 JavaScript 中有一个重要概念 - 异步 (async),它允许我们在执行耗时任务时,不必等待程式完成,而是继续执行下面的代码,直到任务完成再通知。常用的异步操作有:文件操作、数据库操作、AJAX 以及定时器等。 JavaScript 有两种实现异步的方式: 第一种:回调函式 callback function 在 ES6 promise 出现之前,通常使用回调函式 (callback) 实现异步操作。但使用回调函式 callback 存在一个明显的缺点,当需要执行多个异步操作时,代码会不断往内嵌套,这种情况通常被称为「callback 地狱」(callback hell)。 callback(() => { console.log("Hello!"); callback(() => { console.log("Hello!"); callback(() => { console.log("Hello!"); callback(() => { console.log("Hello!"); }, 200); }, 200); }, 200); }, 200); 而为了解决这种问题,就出现了第二种方法 - promise。 什么是 Promise? 上一段提到 Promise 出现的原因,这一段我们来看那到底 Promise 是什么。 ...

October 2, 2024

Generator

聊聊 for of 说起 for of 相信每个写过 JavaScript 的人都用过 for of ,平时我们用它做什么呢?大多数情况应该就是遍历数组了,当然,更多时候,我们也会用 map() 或者 filer() 来遍历一个数组。 但是就像我们标题里面说的,它跟 Generator 能扯上什么关系呢? 首先我们想一个问题,为什么使用 for of 或者 map()/filer() 方法就可以遍历一个数组 (或者类数组对象: Strings , Maps , Sets , arguments ) 呢? 为什么不能用他们来遍历一个对象呢? 你能学到什么 对 for of 更深入的理解 iterator 到底是何方神圣? 数组也是对象,为什么不能用 for of 来遍历对象呢? 如何实现对象的 for of? Generator 又是何方神圣?他有什么用呢? 类数组对象的玄机 在真正揭开谜底之前,站在 for of 的角度想一下,现在让你去遍历一个数组,你需要知道什么信息呢? 对应下标的值 是否遍历结束的标志 带着这样的思考,我们打印一个数组来看看这里面的玄机: const numbersArray = [1, 2, 3]; console.dir(numbersArray); 数组 (或者类数组对象: Strings , Maps , Sets , arguments ) 的原型中都实现了一个方法 Symbol.iterator,问题来了,那么这个 Symbol.iterator 又有什么用呢? 拿出来试一下就知道了: ...

October 2, 2024

Async/Await

async/await用法 其实你要实现一个东西之前,最好是先搞清楚这两样东西 这个东西有什么用? 这个东西是怎么用的? 有什么用? async/await的用处就是:用同步方式,执行异步操作,怎么说呢?举个例子 比如我现在有一个需求:先请求完接口1,再去请求接口2,我们通常会这么做 function request(num) { // 模拟接口请求 return new Promise(resolve => { setTimeout(() => { resolve(num * 2) }, 1000) }) } request(1).then(res1 => { console.log(res1) // 1秒后 输出 2 request(2).then(res2 => { console.log(res2) // 2秒后 输出 4 }) }) 或者我现在又有一个需求:先请求完接口1,再拿接口1返回的数据,去当做接口2的请求参数,那我们也可以这么做 request(5).then(res1 => { console.log(res1) // 1秒后 输出 10 request(res1).then(res2 => { console.log(res2) // 2秒后 输出 20 }) }) 其实这么做是没问题的,但是如果嵌套的多了,不免有点不雅观,这个时候就可以用async/await来解决了 async function fn () { const res1 = await request(5) const res2 = await request(res1) console.log(res2) // 2秒后输出 20 } fn() 是怎么用? 还是用刚刚的例子 ...

October 2, 2024