作用域 (scope)
作用域指的是变数或函式在程式码中的有效範围,有全域和区域两种,全域的作用域是文件中的全部程式码,区域的作用域只在一个block里面
作用域链(scope chain)
在原本的作用域里没有指定的变数,于是继续往父层寻找,直到找到为止,这就是scope chain
什么是 hoisting?
hoisting的特性会出现在函式或变数上,让他们的宣告提升到作用域顶端。在变数提升的特性中,如果在宣告var变数之前使用,会出现undefined,但如果在宣告前使用let和const会因为TDZ的关係报错。在函式提升的特性中,函式表达式的提升行为会与它宣告变数的提升行为一样。var, let, const差别
作用域。var 是global scope或functional scope,let 和const是block scope。var跟let可以被重新赋值,const不行三者都会被hoist,但只有var变数可以在被宣告前使用,const和let会因为TDZ的关係,如果在宣告变数之前就使用他们会报错JS特别要注意的地方(型别转换与小数运算)
console.log((0.1+0.2) === 0.3)的结果是什么?
结果会是falsy,因为电脑是二进位制,电脑表示精準度有限,所以实际上小数点后面会有误差,0.1加上0.2之后不会「刚好」等于0.3
可以使用toFixed解决,设定精确到小数第一位
(0.1+0.2).toFixed(1)
falsy value
falsy value是指一个不完全是false的值,但当我们尝试将它转变成布林值时,我们会得到false。在JavaScript中,有5个falsy values,分别是0、空字串(empty string)、NaN、null及undefined。
弱型别是甚么
js是弱型别的语言,在运算前会进行型别转换,假设转换后发现有字串,就会进行字串的相加,否则就是一般的数字相加。若想都以数字计算,假设有浮点数的话,可用Number(),或假设是字串和数字混杂的话,可用parseInt();另一方面,JS在减法时会将字串转为数值,如果是 const str = "3"
减 const num = 1
的话,会以数字做计算。Typescript是用来解决弱型别的问题
== 与===的差别在哪
两个等号是宽鬆相等,在比较时会做型别转换,假设我们把字串1==数字1,字串会被转换成数字,最后回传true。
三个等号不会进行型别转换,所以如果上面两个值去做严格相等比较,最后回传false
通常不建议用宽鬆相等,可能因为型别转换造成预期外的结果。
闭包
闭包的概念就是,当一个函数内部定义了另一个函式,即便外部函式执行完毕,内部函式依旧可以访问外部函式中的变数const a = 1;console.log(a);const outer = () => { let a = 1; const inner = () => { a += 1; console.log(a); }; return inner;};const result = outer();result(1);//output 2result(1);//output 3
当我们呼叫 result() 时,实际上是在执行了 inner() 函式,而 inner 函式内部的 a 变数形成了一个闭包(Closure)。每次呼叫 result(),都能继续存取并修改 a 的值,且 a 的值不会重新初始化。这是因为 a 变数被 inner 函式的作用域所捕获,使得在每次呼叫 result() 时都能累加之前a的值。
AJAX
同步与非同步、promise是什么?
同步就是按顺序执行,一行程式码执行完才会执行下一行的意思,但它有个问题,如果今天读取档案 (例如是背景图片) 的程式码在最前面,它採用同步执行而且花费超久的时间,使用者可能等很久页面还是一片空白,因为处理事件的流程被「卡住」了
非同步就是不一定要按照顺序执行,如果 JS 没有「非同步」的特性,网页可能会跑两行字,然后去拿图片资源,当图片没有完全被载入,图片后面的文字也无法出现,这对于使用者体验很糟。有了JS「非同步」的特性,网页中的文字会先完全显示出来,等图片被完全载入后再显示出来,在图片出现之前,使用者能阅读网页中的所有文字内容。
Promise是用来解决非同步事件处理,确保非同步事件完成后才继续执行其他程式码,在执行过程中可以看到三种状态,分别是pending(进行中), fulfilled(已成功),跟rejected(已失败),可以用.then()和.catch()语法去实作,更进阶的语法是finally(),代表无论promise状态无论fulfilled或rejected,都会执行finally()里面的程式码,如果网站里有设置在向后端fetch资料时出现loading效果,使用finally()能确保无论资料有无回传,都会关闭loading效果
为何有callback hell、如何解决?
原因是因为在处理非同步程式码时写太多层的callback functions,可以使用async/await 语法提升可读性async await 是什么?
async/await 语法的操作原理和promise一样,所以也被称作Promise 的语法糖,比起Promise的写法更像同步操作。用async 关键字把函式标记为异步函式,在处理promise时加await语法,用try block和catch block分别处理资料处理成功或失败async function getData(){try{ const res = await fetch("example-url",{method:"GET"}); const data = await res.json(); console.log(data);}catch(err){ console.log(err)}}
Axios是什么?使用它的优点为何?
缺点是要安装套件优点是语法更简洁,在写request method时可简化为axios.get("url")
,传资料给server不需用JSON.stringify()
,从server接收资料也不需要用json()
就能使用完整程式码如下//确定已经跑过 npm i axiosconst axios = require('axios');axios.get('url').then(res=>{ console.log('Data', res.data)}).catch(err=>{ console.log(err)})
传参考&传值
如果是基本型别 (Primitive type),原始变数「不会」跟着複製变数的改变而变,表现出的行为是 pass by value。
如果是物件型别,且仅针对物件的内容做改变,原始变数「会」跟着複製变数的改变而有所不同,表现出的行为结果就是 pass by reference。传参考表示变数指向的是同一个记忆体位置,修改其值就会导致共享该记忆体的其他变数一起被修改
const obj1 = { id: 1 };const obj2 = obj1;console.log(obj1 === obj2); //true 两个变数共享同个记忆体位置
但要注意以下情境
let c = {name:'Sally'}let d;d=c;c={name:'Angela'}console.log(c); // {name:'Angela'}console.log(d); // {name:'Sally'}
当我们将一个新的物件赋值给c时,实际上是创建了一个新的物件,并将它的参考赋值给了c。此时,c和d已经没有任何关係了
深浅拷贝
在 JavaScript 中,多数的预设方法和运算子进行的通常是浅拷贝。然而,有一些方法并不是 JavaScript 核心语言的一部分,而是通过函式库或框架提供的,可以执行深拷贝操作
JSON.parse() 和 JSON.stringify()const originalObj = { a: 1, b: { c: 2 } };const deepCopiedObj = JSON.parse(JSON.stringify(originalObj));originalObj.b.c = 100;console.log(deepCopiedObj); // Output: { a: 1, b: { c: 2 } }
深层複製需要耗费更多资源,不一定有必要。对于大多数情况来说,使用展开运算子进行浅层複製已经很够了
展开运算子
展开运算子spread(...)
使用情境
複製现有阵列:如果你想要创建一个现有阵列的複製,你可以使用展开运算符将现有阵列的元素展开到一个新阵列中。例如,const newArray = [...oldArray] 将创建一个新阵列,运用的是浅拷贝。
合併阵列:如果你想要通过合併两个或更多阵列的元素来创建一个新阵列,你可以使用展开运算符将每个阵列的元素展开到一个新阵列中。例如,const newArray = [...array1, ...array2] 将创建一个新阵列,其中包含 array1 的元素,然后是 array2 的元素。
将元素添加到阵列:如果你想要将一个或多个元素添加到现有阵列并创建一个新阵列,你可以使用展开运算符将现有阵列的元素展开到一个新阵列中,然后添加新元素。例如,const newArray = [...oldArray, newItem1, newItem2]
以上若有任何错误,都欢迎留言给我,谢谢