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