动态类型:不需要预先声明变量的类型
解释型语言:运行时解释和执行
基于原型:对象可以继承其他对象的属性
第一类函数
let
const:申明时必须要给它赋值,否则会报错
var:会造成全局对象的污染
数值Number:既可以申明整数,也可以申明浮点数。
NaN:不是数值 (0/0)
Infinity:正无穷大
-Infinity:负无穷大
字符串:双引号,单引号,反引号都可以表示(推荐双引号)
模板字符串:在模板字符串使用 ${} 可以在其中插入变量并解析。
1const name = "0xAA";2const age = 18;3const template = `姓名 ${name},年龄 ${age}。`;4console.log(template); // 姓名 0xAA,年龄 18布尔值 boolean
函数表达式: 它与上面的方式声明几乎一样,唯一的区别就是函数名 sum1 被提到前面作为变量,并且多了赋值操作。使用起来也是一样的。
xxxxxxxxxx31const sum1 = function(x, y){2 return x + y;3}
箭头函数: ES6 版本新增了使用箭头语法 => 来定义函数,这种语法比另外两种方法更为简洁。
xxxxxxxxxx31const sum2 = (x, y) => {2 return x + y;3}
使用规则: 条件 ? 表达式1 : 表达式2。当条件为真时,执行表达式1,否则执行表达式2。该运算符经常当作 if-else 语句的简捷形式来使用。例如:
xxxxxxxxxx31// 返回 x 和 z 之中更大的数2const bigger = x < z ? z : x;3console.log(bigger);
在 JavaScript 中,对象是一种重要的数据类型,用于存储键值对(key-value pairs)和更复杂的实体。对象可以用来表示现实生活中的事物,以及实现面向对象编程的核心概念。
对象是一个无序的键值对集合,其中键是属性的名称,值可以是任意数据类型(包括其他对象、数组或函数)。
示例:
xxxxxxxxxx91javascript复制代码const person = {2name: "Alice",3age: 25,4isStudent: true,5hobbies: ["reading", "gaming"],6greet: function () {7console.log("Hello, my name is " + this.name);8}9};
对象字面量(Object Literal)
xxxxxxxxxx41javascript复制代码const obj = {2key: "value",3anotherKey: 424};
使用 new Object()
xxxxxxxxxx31javascript复制代码const obj = new Object();2obj.key = "value";3obj.anotherKey = 42;
使用构造函数
xxxxxxxxxx51javascript复制代码function Person(name, age) {2this.name = name;3this.age = age;4}5const person = new Person("Bob", 30);
使用 Object.create()
xxxxxxxxxx31javascript复制代码const prototype = { type: "human" };2const obj = Object.create(prototype);3obj.name = "Charlie";
对象属性可以通过点表示法或方括号表示法访问。
点表示法
x1javascript234复制代码5console.log(person.name); // "Alice"
方括号表示法
(当属性名包含特殊字符或变量名时)
xxxxxxxxxx51javascript234复制代码5console.log(person["name"]); // "Alice"
添加属性
xxxxxxxxxx51javascript234复制代码5person.gender = "female";
删除属性
xxxxxxxxxx51javascript234复制代码5delete person.age;
使用 for...in
xxxxxxxxxx31javascript复制代码for (let key in person) {2console.log(key, person[key]);3}
使用 Object.keys()
xxxxxxxxxx31javascript复制代码Object.keys(person).forEach(key => {2console.log(key, person[key]);3});
Object.keys(obj):获取对象的所有键
xxxxxxxxxx51javascript234复制代码5console.log(Object.keys(person)); // ["name", "age", "isStudent", "hobbies", "greet"]
Object.values(obj):获取对象的所有值
xxxxxxxxxx51javascript234复制代码5console.log(Object.values(person)); // ["Alice", 25, true, ["reading", "gaming"], function]
Object.entries(obj):获取键值对数组
xxxxxxxxxx21javascript复制代码console.log(Object.entries(person));2// [["name", "Alice"], ["age", 25], ["isStudent", true], ["hobbies", ["reading", "gaming"]], ["greet", function]]
Object.assign(target, ...sources):合并对象
xxxxxxxxxx21javascript复制代码const newPerson = Object.assign({}, person, { age: 30 });2console.log(newPerson);
Object.freeze(obj) 和 Object.seal(obj)
Object.freeze():冻结对象,禁止修改属性。
Object.seal():禁止添加或删除属性,但允许修改现有属性值。
对象可以包含函数作为属性,称为方法。
xxxxxxxxxx71javascript复制代码const car = {2brand: "Toyota",3start: function () {4console.log(this.brand + " is starting...");5}6};7car.start(); // Toyota is starting...
this 关键字this 在对象中指代当前对象。
xxxxxxxxxx71javascript复制代码const person = {2name: "Alice",3greet: function () {4console.log("Hello, I am " + this.name);5}6};7person.greet(); // Hello, I am Alice
对象是引用类型,直接赋值会复制引用地址,而不是值。
示例:
xxxxxxxxxx41javascript复制代码const obj1 = { key: "value" };2const obj2 = obj1;3obj2.key = "newValue";4console.log(obj1.key); // "newValue"
通过解构语法快速提取对象中的属性。
xxxxxxxxxx31javascript复制代码const { name, age } = person;2console.log(name); // "Alice"3console.log(age); // 25
JavaScript 对象与 JSON(JavaScript Object Notation)类似,但 JSON 只允许字符串作为键,且是文本格式。
将对象转为 JSON:JSON.stringify()
xxxxxxxxxx21javascript复制代码const jsonString = JSON.stringify(person);2console.log(jsonString);
将 JSON 转为对象:JSON.parse()
xxxxxxxxxx21javascript复制代码const jsonObject = JSON.parse(jsonString);2console.log(jsonObject);
闭包(Closure) 是 JavaScript 中一个非常重要的概念,它允许函数访问其词法作用域,即使这个函数是在其词法作用域之外执行的。闭包是 JavaScript 中实现数据封装、私有变量等的核心机制。
闭包是指一个函数能够记住并访问它的词法作用域,即使这个函数是在当前词法作用域之外执行的。换句话说,闭包使得内部函数始终可以访问其外部函数的变量。
JavaScript 的作用域是词法作用域,意味着函数的作用域是在函数定义时确定的,而不是在函数调用时确定的。闭包正是利用了这种特性,使得函数可以“记住”它在定义时的作用域。
xxxxxxxxxx121function outer() {2 const outerVar = 'I am an outer variable';3
4 function inner() {5 console.log(outerVar); // inner 函数可以访问 outer 函数的变量6 }7
8 return inner;9}10
11const closureFunction = outer(); // outer 执行后返回 inner 函数12closureFunction(); // 输出:I am an outer variable解释:
outer 函数:定义了一个变量 outerVar 和一个内部函数 inner。
inner 函数:可以访问 outerVar,因为它定义在 outer 的内部,形成了一个闭包。
当 outer 返回了 inner 函数时,outer 的执行上下文被销毁,但由于闭包的存在,inner 依然能够访问 outerVar。
访问外部函数的变量
闭包能够记住外部函数中的变量,即使外部函数执行完毕,内部函数依然可以访问这些变量。
模拟私有变量
JavaScript 没有内置的私有变量机制,但是闭包可以用于创建私有变量。
通过在函数中使用局部变量并返回操作这些变量的函数,可以使变量在函数外部不可直接访问。
示例:模拟私有变量
xxxxxxxxxx231function counter() {2 let count = 0; // 私有变量3
4 return {5 increment: function () {6 count++;7 console.log(count);8 },9 decrement: function () {10 count--;11 console.log(count);12 },13 getCount: function () {14 return count;15 }16 };17}18
19const myCounter = counter();20myCounter.increment(); // 输出: 121myCounter.increment(); // 输出: 222myCounter.decrement(); // 输出: 123console.log(myCounter.getCount()); // 输出: 1解释:
count 是 counter 函数中的局部变量,外部无法直接访问它。
通过返回的对象中的 increment、decrement、getCount 方法,可以间接地操作 count,实现类似私有变量的效果。
数据封装和私有化
闭包可以用来创建私有数据,避免全局变量污染。
回调函数和事件处理
在回调函数中,闭包可以记住定义时的上下文,避免作用域丢失的问题。
延迟执行
闭包也用于需要延迟执行的场景,比如定时器或异步操作。
示例:延迟执行
xxxxxxxxxx71function delayedMessage(message, delay) {2 setTimeout(function () {3 console.log(message);4 }, delay);5}6
7delayedMessage("Hello after 2 seconds", 2000); // 2秒后输出: Hello after 2 seconds在上面的例子中,匿名函数通过闭包访问了参数 message,即使 delayedMessage 函数已经执行完毕。
循环中的闭包问题
在使用 for 循环配合闭包时,可能遇到变量共享的问题。
在 ES5 中,可以使用立即执行函数(IIFE)解决;在 ES6 中,可以使用 let 关键字,因为 let 有块作用域。
示例:循环中的闭包问题
xxxxxxxxxx251for (var i = 0; i < 3; i++) {2 setTimeout(function () {3 console.log(i);4 }, 1000);5}6// 输出: 3, 3, 3(所有定时器都共享了 `i`)7
8// 解决方法:使用 IIFE 或 let9for (var i = 0; i < 3; i++) {10 (function (j) {11 setTimeout(function () {12 console.log(j);13 }, 1000);14 })(i);15}16// 输出: 0, 1, 217
18// 或者使用 ES6 的 let19for (let i = 0; i < 3; i++) {20 setTimeout(function () {21 console.log(i);22 }, 1000);23}24// 输出: 0, 1, 225
由于闭包会保留对外部函数作用域中变量的引用,这可能导致这些变量在函数执行结束后依然存在,造成 内存泄漏。因此,在使用闭包时,需要注意:
避免不必要的闭包:不要随意在全局环境中创建闭包,以免意外地占用内存。
解除引用:当闭包不再需要时,可以通过将引用设置为 null 的方式来帮助垃圾回收。