50 个最常见的 JavaScript 面试问题
目录
你一直在学习 JavaScript,并计划申请工作。回答 50 个最常见的 JavaScript 面试问题,为面试做好准备。
目录
- 什么是 JavaScript?
- JavaScript 中的原始类型和引用类型有什么区别?
- == 和 === 有什么区别?
- 什么是高阶函数?
- 什么是纯函数?
- 什么是函数柯里化?
- var、let 和 const 之间有什么区别?
- 全局范围和局部范围有什么区别?
- 什么是暂时性死区?
- 什么是提升?
- 什么是闭包?
- 如何比较两个对象?
- 在 JavaScript 中创建对象的所有可能方法有哪些?
- 什么是原型链?
- 调用、应用和绑定之间有什么区别?
- 什么是JSON以及它的常见操作?
- 数组切片方法的用途是什么?
- 数组拼接方法的用途是什么?
- 切片和拼接有什么区别?
- 什么是 lambda 或箭头函数?
- 什么是 IIFE(立即调用函数表达式)?
- 如何在 JavaScript 中解码或编码 URL?
- 什么是记忆?
- ES6 中的类是什么?
- 什么是模块?
- 为什么需要模块?
- 什么是承诺
- 承诺的三个状态是什么?
- 什么是回调函数?
- 为什么我们需要回调?
- 什么是回调地狱以及如何避免它?
- 什么是承诺链?
- 什么是 promise.all?
- promise 中的 race 方法的目的是什么?
- javascript 中的严格模式是什么?
- 为什么需要严格模式?
- 如何声明严格模式?
- 删除操作符的用途是什么?
- typeof 运算符是什么?
- 什么是未定义的?
- 什么是 null?
- null 和 undefined 有什么区别?
- 什么是 eval?
- 窗口和文档有什么区别?
- 如何在 javascript 中访问历史记录?
- 如何检测大写锁定键是否打开?
- isNaN 是什么?
- 未声明变量和未定义变量之间有什么区别?
- 什么是全局变量?
- 全局变量有什么问题?
什么是 JavaScript?
JavaScript 是一种动态类型、解释型脚本语言。它支持多种编程范式,例如函数式、面向对象编程 (OOP) 和过程式编程 (PHP)。它是唯一一种直接在浏览器上运行的语言,因此也被称为 Web 语言。
- 动态类型 - 意味着变量在其整个生命周期内可以保持多种类型
let someVarName = "JavaScript" // JavaScript
someVarName = 12 // 12
someVarName = {name: "JavaScript"} // {name: "JavaScript"}
someVarName = false // false
someVarName = [1,2,3] // [1,2,3]
- 解释型 - 意味着 JavaScript 代码不是像 C、C++ 和 Java 等语言那样先编译后执行,而是使用 JIT 编译器执行
JavaScript 中的原始类型和引用类型有什么区别?
原始类型 原始
类型只能保存一个值,而非原始类型可以保存多个值。JavaScript
原始类型
""
false
1
null
undefined
123n
Symbol("symbol")
非原始类型或引用类型
非原始类型可以同时包含多个原始值
const hobbies = ["Reading", "Calisthenics", "Swimming"]
const person = {
name: "Jaxongir",
age: 27,
country: "Uzbekistan"
hobbies
}
== 和 === 有什么区别?
== - 比较运算符仅比较值而不比较其类型。如果比较两个不同类型的相同值,则会发生类型转换。即将一种类型转换为另一种类型。
console.log(1 == "1") // true
console.log(false == "0") // true
=== - 严格相等比较运算符首先比较值的数据类型。如果它们是同一类型,则比较值。否则,无论值是否相同,它们都不是同一类型,都返回 false。
console.log(1 === "1") // false
console.log(false === "0") // false
什么是高阶函数?
HOF(高阶函数) - 是将另一个函数作为参数并在其主体内调用它或返回函数作为值的函数
。HOF 的示例有:map、filter、for each、reduce 等...
const nums = [1,2,3,4]
const multiplyNum = nums.map(num => num * 2)
console.log(multiplyNum) // [2,4,6,8]
const greet = (greeting)=> name => `${greeting} ${name}!`
const func = greet("Hello")
func("Jaxongir") // Hello Jaxongir!
func("Lola") // Hello Lola!
什么是纯函数?
纯函数——是没有任何副作用的函数,它永远不会改变参数并返回与参数相同类型的副本。
// Impure function
const add = (num1, num2)=> {
num1 = 10
return num1 + num2
}
// Pure function
const add = (num1, num2)=> num1 + num2
什么是函数柯里化?
Currying是一种函数式编程技术,将具有多个参数的函数转换为具有单个参数的多个函数
const curry = (f) => (a) => (b) => f(a, b);
const product = (num1, num2) => num1 * num2;
const curriedProduct = curry(product);
console.log(curriedProduct(20)(5)) // 100
console.log(curriedProduct(1)(10)) // 10
console.log(curriedProduct(0)(100)) // 0
var、let 和 const 之间有什么区别?
let 和 const
- let 和 const 是 2015 年推出的 ES6 特性
- 使用 let 或 const 声明的变量的作用域是 {},这意味着它们在定义的 {} 之外是不可见的
- 用它们声明的变量不会被提升,这意味着我们不能在它们定义之前访问它们
- var 是 ES5 特性,
- 如果变量在函数内用 var 关键字声明,那么它的作用域就在该函数内,这意味着它不能在函数体之外访问它的定义
- 在所有其他情况下,用 var 关键字声明的变量都是全局作用域的
if(10 > 0){
var test = "right"
let test2 = "test"
}
console.log(test) // right
console.log(test2) // ReferenceError: test2 is not defined
全局范围和局部范围有什么区别?
全局作用域- 变量和函数可全局访问。因此,当你在任何 {} 之外声明 let 和 const 变量时,它们的作用域是全局的。而当在函数外部用 var 声明的变量是全局作用域时,
局部作用域- 只要用 let 和 const 声明的变量位于 {} 内,它们的作用域就是该 {} 内部的局部作用域。
// global scope variable
const name = "Jaxongir"
// global scoped function
const func = ()=>{
// local scoped variable
let age = 25
}
func()
console.log(name) // Jaxongir
console.log(age) // ReferenceError: age is not defined
什么是暂时性死区?
临时死区- 是指用 let 或 const 声明的变量无法访问。发生这种情况是因为它们没有被提升。或者更简单地说,它们在作用域为 {} 到为其分配内存期间是不可见的,或者处于临时死区中。
const fuc = () => {
console.log(name);
console.log(age);
var age = 27;
let name = "Jaxongir";
};
fuc() // ReferenceError: Cannot access 'name' before initialization
什么是提升?
提升- 是指用 var 和函数声明声明的变量在执行之前移动到当前范围的顶部,因此它们在声明之前就可以访问。
greeting("Jaxongir")
function greeting(name) {
console.log("Hello " + name) // Hello Jaxongir
console.log(age) // undefined
var age = 26;
}
什么是闭包?
闭包——本质上是指函数即使在其周围作用域已经执行之后,也能始终访问其周围作用域。每次创建函数时,都会创建一个闭包。每次创建函数时,该函数都可以访问其周围作用域中定义的变量、函数和对象。
let name = "Jaxongir";
const fun = () => {
console.log(name) // Jaxongir
};
fun()
const outerFunc = (message)=>{
let test = "string"
const innerFunc = (text)=>{
console.log(message, test, text)
}
}
const test = outerFunc("Hello")
test("JavaScript")
如何比较两个对象?
如需深入解释,请查看这个优秀的stack overflow
const obj1 = {name: "Lola"}
const obj2 = {name: "Lola"}
JSON.stringify(obj1) === JSON.stringify(obj2) // true
在 JavaScript 中创建对象的所有可能方法有哪些?
// object literals
const person = {name: "Jaxongir}
// Object constructor
const person = new Object();
console.log(person);
// Object create method
const person = Object.create({});
console.log(person);
// singleton pattern
const person = new (function () {
this.name = "Jaxongir";
})();
// Constructor function
function Person(name) {
this.name = name;
}
const person = new Person("Jaxongir");
// ES6 Class
class Person {
constructor(name) {
this.name = name;
}
}
const person = new Person("Jaxongir");
console.log(person);
什么是原型链?
原型链- 对象从其原型对象继承其属性和方法
JavaScript 中所有其他类型继承的根构造函数都是对象
const company = {
companyName: "UZS",
};
const teacher = {
fullname: "Jaxongir Rahimov",
__proto__: company,
};
console.log(teacher.companyName) // UZS
console.log(teacher.fullname) // Jaxongir Rahimov
调用、应用和绑定之间有什么区别?
虽然它们在使用方式上有所不同,但它们的共同点在于,它们都在函数体内为 this 关键字提供上下文,并传入参数。
注意:它们只能在函数声明时调用,而不能在箭头函数中调用。因为箭头函数不绑定 this 关键字。
call - 使用提供的 this 值和其他参数调用函数。第一个参数始终是 this 的上下文或对象,并且可以接受 n 个参数
const person1 = {
name: "Jaxongir",
age: 27,
country: "Uzbekistan",
gender: "male",
hobbies: ["Reading", "Calisthenics", "Swimming"],
};
const person2 = {
name: "Lola",
age: 21,
country: "Russia",
gender: "female",
hobbies: ["Reading", "Knitting", "Swimming", "Badminton"],
};
function printBio(greeting) {
console.log(
`${greeting} ${this.gender === "male" ? "His name is" : "Her name is"} ${
this.name
} and is ${this.age} years old and is from ${
this.country
} and has following hobbies ${this.hobbies.join(", ")}`
);
}
printBio.call(person1, "Hello") // Hello His name is Jaxongir and is 27 years old and is from Uzbekistan
printBio.call(person2, "Hello") // Hello Her name is Lola and is 21 years old and is from Russia
apply - 调用提供此值上下文的函数并传递参数数组
function printBio(greeting) {
console.log(
`${greeting} ${this.gender === "male" ? "His name is" : "Her name is"} ${
this.name
} and is ${this.age} years old and is from ${this.country}`
);
}
printBio.apply(person1, ["Hello"]);
printBio.apply(person2, ["Hello"]);
bind - 返回可以存储变量的新函数,当调用该函数时,将其设置为提供的对象并传递值
function printBio(greeting) {
console.log(
`${greeting} ${this.gender === "male" ? "His name is" : "Her name is"} ${
this.name
} and is ${this.age} years old and is from ${this.country}`
);
}
printBio.bind(person1)("Hello");
printBio.bind(person2)("Hello");
什么是JSON以及它的常见操作?
JSON是 JavaScript 对象表示法 (Object Notation) 的缩写,用于在网络上发送数据。即使是雅达利 (Atari) 前员工、发明并推广“JSON”一词的 Douglas Crockford,也声称是他“发现”了 JSON,而不是“发明”了
它。JSON 主要包含两个操作:
解析- 将文本转换为原始数据
JSON.parse("{name: "Jaxongir"}") // {name: "Jaxongir"}
字符串化——将有效数据转换为字符串格式
JSON.stringify({name: "Jaxongir"}) // "{name: "Jaxongir"}"
数组切片方法的用途是什么?
数组切片方法用于复制字符串和数组的一部分,以便我们可以直接操作数组副本,而不是修改它。它用于防止数据突变。这是切片方法的链接。
let fullname = "Jaxongir Rahimov"
console.log(fullname.slice(0, 10))
const people = ["Lola", "Jol", "Mat", Jaxongir"]
conosle.log(people.slice(0,2))
数组拼接方法的用途是什么?
数组拼接方法用于删除给定索引中的指定项目或在给定索引中添加单个或多个项目。链接到拼接
const people = ["Lola", "Jaxongir", "Test", "Horum"];
// Deleting single item in the given index
console.log(people); // ["Lola", "Jaxongir", "Test", "Horum"]
people.splice(2, 1);
console.log(people); // ["Lola", "Jaxongir", "Horum"]
// Adding multiple items in the given index
people.splice(2, 0, "Madina", "Nodira");
console.log(people); // ["Lola", "Jaxongir", "Madina", "Nodira", "Horum"]
切片和拼接有什么区别?
slice - 返回数组的副本,并且不会改变数组
const people = ["Lola", "Jaxongir", "Test", "Horum"];
console.log(people.slice(0,2)) // ["Lola", "Jaxongir")
console.log(peopl) // ["Lola", "Jaxongir", "Test", "Horum"]
splice - 删除一个或多个项目或添加一个或多个项目,并进行这些更改
const people = ["Lola", "Jaxongir", "Test", "Horum"];
people.splice(2, 1);
console.log(people); // ["Lola", "Jaxongir", "Horum"]
什么是 lambda 或箭头函数?
箭头函数- ES6 特性,与普通函数声明有一些区别
- 箭头函数没有 this 关键字,但它使用其周围范围内 this 的任何值
- 由于上述原因,箭头函数不能用于创建构造函数
- 箭头函数没有超级参数
const person = {
name: "Jaxongir",
test: () => {
console.log(arguments) // ReferenceError: arguments is not defined
console.log(this) // references to window object
},
test2() {
console.log(arguments) // [2, 13, 321, 2]
console.log(this) // {name: 'Jaxongir', test: ƒ, test2: ƒ}
},
};
person.test();
person.test2(2, 13, 321, 2);
什么是 IIFE(立即调用函数表达式)?
IIFE - 顾名思义,它是一个函数,声明后立即执行。它主要用于创建模块设计模式、单例设计模式。
const person = (() => {
let name = "Jaxongir";
let age = 27;
return {
name,
age,
};
})();
console.log(person) // {name: "Jaxongir", age: 27}
如何在 JavaScript 中解码或编码 URL?
encodeURI - 以 url 字符串作为参数并对其进行编码,然后返回编码后的 URI。decodeURI
-以编码后的 URI 为参数并返回解码后的 url。
const uri = "https://mozilla.org/?x=шеллы";
const encoded = encodeURI(uri);
console.log(encoded); // https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B
console.log(decodeURI(encoded)) // https://mozilla.org/?x=шеллы
什么是记忆?
记忆化——是一种编程技术,它通过缓存开销较大的函数调用结果,并在出现相同输入时返回缓存数据来优化应用程序的性能。并且,只有当输入不同时,才会执行相同的开销较大的函数计算。
const fib = (num, memo = []) => {
if (memo[num]) return memo[num];
if (num <= 2) return 1;
const res = fib(num - 1, memo) + fib(num - 2, memo);
memo[num] = res;
return res;
};
console.log(fib(5));
ES6 中的类是什么?
ES6 - 类允许像 Java 或其他 OOP 语言一样以 OOP 风格编写 JavaScript 程序。虽然 ES6 的类在底层被转换为构造函数,并且引入了原型继承,但编写 OOP 风格的代码仍然更容易,尤其是对于那些曾经使用过 OOP 语言的人来说。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat(food) {
console.log(`${this.name} eats ${food}`);
}
sleep(time) {
console.log(`${this.name} sleeps at ${time}`);
}
wakeup(time) {
console.log(`${this.name} wakesup at ${time}`);
}
}
class Teacher extends Person {
constructor(name, age, role, salary, hobbies) {
super(name, age);
this.role = role;
this.salary = salary;
this.hobbies = hobbies;
}
printBio() {
console.log(
`${this.name} is ${this.age} years old. And he is ${
this.role
} with the salary of $${
this.salary
}. His hobbies are: ${this.hobbies.join(", ")}`
);
}
}
const jaxongir = new Teacher("Jaxongir", 27, "Full-Stack mentor", 1500, [
"Reading",
"Calisthenics",
"Swmming",
]);
jaxongir.eat("Caviar") // Jaxongir eats Caviar
jaxongir.sleep("23:00 pm") // Jaxongir sleeps at 23:00 pm
jaxongir.wakeup("09:00 am") // Jaxongir wakesup at 09:00 am
jaxongir.printBio() // Jaxongir is 27 years old. And he is Full-Stack mentor with the salary of $1500. His hobbies are: Reading, Calisthenics, Swmming
什么是模块?
模块- 是相关可重用代码的容器,可以导入到多个文件中。
为什么需要模块?
模块对于数据隐私、模块化、可维护性、命名空间以及代码的可重用性都非常有用。基本上,它们允许我们独立地思考软件,从而减轻大脑的负担,并使我们更容易思考程序的整体结构。
什么是承诺
Promise - 指示异步操作结果的对象,该结果可能是失败(被拒绝)或成功(被实现)。
const promise = new Promise(function (resolve, reject) {
// promise description
});
const promise = new Promise(
(resolve) => {
setTimeout(() => {
resolve("I'm a Promise!");
}, 5000);
},
(reject) => {}
);
promise.then((value) => console.log(value));
承诺的三个状态是什么?
Promise的3种状态描述如下:
- 已完成 - 当异步请求成功完成并给出响应时
- 拒绝 - 当异步请求不成功且没有给出响应时,这可能是由于网络错误、身份验证或授权错误等造成的
- 待处理 - 处于发送请求和返回响应之间
什么是回调函数?
回调- 是作为参数传递给不同函数的函数,然后在该函数体内调用
const nums = [1, 2, 3, 4, 5];
const myMap = (callbackFunc, nums) => {
newNums = [];
for (const num of nums) {
newNums.push(callbackFunc(num));
}
return newNums;
};
const modifiedNums = myMap((num) => num * num, nums);
console.log(modifiedNums) // [1, 4, 9, 16, 25]
为什么我们需要回调?
回调函数在异步操作中非常有用,例如:当数据获取成功或失败时,回调函数会被调用来指示请求成功还是失败。或者在用户点击按钮的事件中,回调函数会被触发并执行回调函数体中的代码。
const generateReportBtn = document.querySelector(".generate-report");
const generateReport = ()=>{
// do something
}
generateReportBtn.addEventListener("click", generateReport)
const populatePeople = ()=>{
// do something
}
setTimeout(populatePeople, 2000)
什么是回调地狱以及如何避免它?
回调地狱——指的是回调函数嵌套过深,这使得代码调试更加困难,因为即使是代码作者也无法理解。解决方案是使用 async await 或 promise 链式调用。
回调地狱的例子。这让我做噩梦。
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})
什么是承诺链?
Promise Chaining —— 是我们上面提到的回调地狱的解决方案。我们不再将一个回调嵌套在另一个回调中,而是使用前一个已完成的 Promise 的结果,并返回链中下一个 Promise 已完成的 Promise。
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1
return result * 2;
}).then(function(result) { // (***)
alert(result); // 2
return result * 2;
}).then(function(result) {
alert(result); // 4
return result * 2;
});
什么是 promise.all?
Promise.all - 是一个静态方法,它接受一个可迭代 Promise 数组作为输入,并返回一个单独的 Promise,该 Promise 数组包含所有已实现的 Promise 的值。已实现的 Promise 的顺序与它们对应的可迭代 Promise 的顺序相同。如果任何一个 Promise 被拒绝,则所有操作都会被拒绝。即使 Promise 数组中的最后一个 Promise 被实现,Promise.all() 也不会停止操作,而是会等待所有 Promise 都实现。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// Expected output: Array [3, 42, "foo"]
promise 中的 race 方法的目的是什么?
Promise.race - 是一种静态方法,它将可迭代的承诺作为输入并返回第一个已解析的承诺值。
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster
});
// Expected output: "two"
javascript 中的严格模式是什么?
严格模式- 在 JavaScript 中,用于将 JavaScript 环境设置为更严格的模式,以避免诸如未使用关键字 var、let、const 声明变量等错误。可以针对整个环境或特定函数启动严格模式。
为什么需要严格模式?
我们需要严格模式,因为:
- 通过将某些 JavaScript 静默错误更改为抛出错误来消除它们。
- 修复了导致 JavaScript 引擎难以执行优化的错误:严格模式代码有时可以比非严格模式的相同代码运行得更快。
- 禁止一些可能在 ECMAScript 未来版本中定义的语法。
如何声明严格模式?
我们只需在全局或特定函数中输入“use strict”即可启用严格模式。
// Non strict mode
age = 27;
console.log(age) // 27
// Strict mode
"use strict";
age = 27;
console.log(age) // ReferenceError: age is not defined
删除操作符的用途是什么?
删除运算符-用于删除对象的属性。如果该属性值是一个对象,那么对该对象的引用就会丢失。
const person = {
name: "John",
age: 34,
country: "USA",
};
console.log(person) // {name: "John", age: 34, country: "USA"}
delete person.country;
console.log(person) // {name: "John", age: 34}
typeof 运算符是什么?
typeof 运算符- 是一个函数,用于了解传递给它的表达式的类型
console.log(typeof 1) // number
console.log(typeof "hello") // string
console.log(typeof false) // boolean
console.log(typeof []) // object
console.log(typeof {}) // object
console.log(typeof undefined) // undefined
console.log(typeof null) // object
console.log(typeof NaN) // number
什么是未定义的?
undefined - 是原始数据类型。当变量声明了但没有给出值时,控制台输出的结果为 undefined。
let name;
console.log(name) // undefined
什么是 null?
null - 也是原始数据类型。它用于表示值不存在。
let name = null;
console.log(name) // null
null 和 undefined 有什么区别?
*无效的
- null 是原始数据类型,表示内存中没有为变量分配内存
- 用于比较时转换为 0
- typeof null 是一个对象
不明确的
- undefined - 也是原始数据类型,表示为变量分配了内存,并且已声明,但未分配任何值
- 用于比较时转换为 NaN
- typeof undefined 是未定义的
什么是 eval?
eval - 用于将代码字符串作为 JavaScript 脚本执行的函数。代码字符串可以是表达式、函数调用等。
警告:通过字符串执行 JavaScript 代码存在巨大的安全风险。使用 eval() 时,恶意攻击者很容易运行任意代码 - MDN
const add = (num1, num2) => num1 + num2;
console.log(eval("add(1, 9)")); // 10
窗口和文档有什么区别?
窗户
- window 对象是每个页面上的根级元素
- 默认情况下,每个页面都可用
- 它有确认、警报等方法
- document 或 DOM 是其自身的直接子属性
文档
- document 或 DOM 是 window 对象的直接子对象,它是 HTML 的表示
- 可以通过 document 或 window.document 引用
- 它让我们通过诸如 querySelector、getElementById、querySelectorAll 等方法访问 DOM 元素
- 它让我们在 UI 上进行 CRUD
如何在 javascript 中访问历史记录?
我们可以通过使用窗口对象的history对象来查看页面历史记录。history对象有back和forward方法。
// moves back to previous URI
window.history.back()
// moves forward to next URI
window.history.forward()
如何检测大写锁定键是否打开?
我们可以使用 KeyboardEvent.getModifierState() 来检测大写锁定键是否被激活。该函数返回布尔值 true 表示大写锁定键被激活,false 表示未激活。它不仅可以检测大写锁定,还可以检测滚动锁定 (ScrollLock) 和数字锁定 (NumsLock)。
const heading = document.querySelector("h1");
document.body.addEventListener("click", (e) => {
const isCapsLockOn = e.getModifierState("CapsLock");
if (isCapsLockOn) {
heading.textContent = "CapsLock is Activated";
} else {
heading.textContent = "CapsLock is Deactivated";
}
});
isNaN 是什么?
isNaN - 内置函数,用于检查给定的输入是否为 NaN(非数字)
console.log(isNaN("1")) // false it's a number;
console.log(isNaN("st")) // true it's not a number
未声明变量和未定义变量之间有什么区别?
未申报
- 变量是未声明或未赋值的变量,当尝试访问它时,会抛出错误
- 由于它们尚不存在,因此没有为它们分配内存
不明确的
- 变量是已声明但未分配值的变量。
- 内存已分配,当尝试在控制台中打印时,声明为未定义
什么是全局变量?
全局变量-是在 {} 和函数之外声明的变量,它们可以在任何地方访问
// global variable
let name = "Jaxongir"
(()=>{
console.log(name) // Jaxongir
})()
全局变量有什么问题?
全局变量存在许多问题,例如:
- 命名空间冲突
- 可维护性
- 可测试性