JS高级知识总结
文章目录
- 1. this指向问题
- 2. 对象进阶
- 2.1 对象的定义和使用
- 2.2 对象访问器
- 2.2.1 Getter
- 2.2.2 Setter
- 2.3 对象构造器
- 2.4 对象原型
- 2.4.1 prototype属性
- 2.4.2 \_\_proto\_\_ 属性
- 2.4.3 constructor属性
- 2.4.4 原型链
- 2.5 Object对象
- 2.5.1 管理对象
- 2.5.2 保护对象
- 3. 函数进阶
- 3.1 函数的定义和使用
- 3.2 Function对象
- 3.2.1 call
- 3.2.2 apply
- 3.2.3 bind
- 3.3 高阶函数
- 3.4 闭包
- 4. 异常
- 5. JSON
- 5.1 JSON语法
- 5.2 JSON解析
- 5.3 JSON字符串化
- 6. 拷贝
- 6.1 浅拷贝
- 6.2 深拷贝
- 7. ES6
1. this指向问题
this指向:this的指向在函数定义是无法确定的,只有在函数执行时才能确定this到底指向谁,一般情况下this的指向就是调用它的对象。
一般分为如下三种情况:
- 全局作用域或普通函数中,this指向全局对象window
- 方法调用中谁调用this指向谁
- 构造函数中this指向构造函数的实例
1.全局作用域或普通函数中,this指向全局对象window
全局作用域中的函数调用,是window对象调用的,只不过调用的时候,一般会省略掉
window.,也就是fn()其实就是window.fn
console.log(this); // Window {window: Window, …}
function fn() {console.log(this); // Window {window: Window, …}
}fn(); // 等同于window.fn()
// 等同于window.setTimeout()
setTimeout(function () {console.log(this); // Window {window: Window, …}
}, 1000);
2.方法调用中谁调用this指向谁
obj = {fn: function () {console.log(this); // {fn: ƒ}}
};obj.fn(); // obj对象调用了fn方法
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><button>点击一下</button><script>var btn = document.querySelector("button");// btn调用了匿名函数btn.addEventListener("click", function () {console.log(this); // <button>点击一下</button>});</script></body>
</html>
3.构造函数中this指向构造函数的实例
创建对象时,会开辟一个新空间,this会指向这个对象
function Person() {console.log(this); // Person {}
}var person = new Person(); // this指向的是person实例对象
2. 对象进阶
2.1 对象的定义和使用
1.使用字面量创建对象
// 创建对象
var obj = {name: "Bill",age: 18,sex: "男",sayHi: function () {console.log("hi");}
};// 获取对象的属性
console.log(obj.age);
console.log(obj["age"]);// 调用对象的方法
obj.sayHi();
2.使用new关键字创建对象
// 创建空对象
var obj = new Object(); // 设置对象的属性和方法
obj.name = "Bill";
obj.age = 18;
obj.sex = "男";
obj.sayHi = function () {console.log("hi");
};// 获取对象的属性
console.log(obj.age);
console.log(obj["age"]);// 调用对象的方法
obj.sayHi();
注:不建议使用new关键字创建对象的方式
2.2 对象访问器
Getter和 Setter的作用:
- 提供了更简洁的语法
- 允许属性和方法的语法相同
- 可以确保更好的数据质量
注:
getter和setter的方法名不能与属性名相同
2.2.1 Getter
Getter:使用 get关键词,来获取对象的属性值。
var person = {name: "Bill",age: 18,get uname() {return this.name;},get uage() {return this.age;}
};console.log(person.uname); // Bill
console.log(person.uage); // 18
2.2.2 Setter
Setter:使用 set关键词,来设置对象的属性值。
var person = {name: "Bill",age: 18,set uname(value) {this.name = value;},set uage(value) {this.age = value;}
};person.uname = "Jackson";
person.uage = 20;
console.log(person.name, person.age); // Jackson 20
2.3 对象构造器
对象构造器:就是构造函数,通过 new 关键词调用构造函数,可以创建相同类型的对象。
构造函数的作用:使用2.1节中的创建对象的方式,只能创建单一对象,而通过构造函数,可以创建许多相同类型的对象。
1.使用构造函数,创建多个相同类型的对象
// 1.定义构造函数
function Person(name, age, sex) {this.name = name;this.age = age;this.sex = sex;this.sayHi = function () {console.log("hi");};
}// 2.调用构造函数来创建对象
var teacher = new Person("Jackson", 40, "male");
var student = new Person("Bill", 18, "male");// 3.获取对象的属性
console.log(teacher.name); // Jackson
console.log(student.name); // Bill// 4.调用对象的方法
teacher.sayHi(); // hi
student.sayHi(); // hi
分析:使用构造函数创建了两个对象实例
teacher和student,两个对象实例拥有着相同的属性和方法
2.为对象添加属性和方法
function Person(name, age, sex) {this.name = name;this.age = age;this.sex = sex;this.sayHi = function () {console.log("hi");};
}var student = new Person("Bill", 18, "male");// 为对象添加属性
student.nationality = "English";// 为对象添加方法
student.sayHello = function () {console.log("hello");
};console.log(student);
2.4 对象原型
构造函数和原型对象:在声明了一个构造函数后,构造函数就会拥有一个prototype属性,该属性向的就是这个构造函数的原型对象。
原型对象的作用:构造函数的方法会存在浪费内存的问题,而构造函数通过原型分配的方法是所有对象所共享的,也就是可以对同一块内存进行复用,避免了浪费内存。
与原型相关的几个属性:
prototype__proto__constructor
2.4.1 prototype属性
prototype:每一个构造函数都有一个prototype属性,指向另一个对象。这个prototype就是一个对象(原型对象),这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
总结:
1.原型是一个对象
2.原型对象的作用是实现了方法的共享
1.构造函数每 new一个对象实例,都会开辟新的内存空间,当把方法定义在构造函数内部的时候,就会造成内存浪费。
function Person(name, age) {this.name = name;this.age = age;this.sayHi = function () {console.log("hi");};
}var teacher = new Person("Jackson", 40);
var student = new Person("Bill", 18);
console.log(teacher.sayHi === student.sayHi); // false
解析:在创建teacher 和 student 两个对象实例的时候,两个实例开辟了不同的内存空间(包含了name、age属性和sayHi 方法),因此这两个实例方法指向的地址是不同的,对比后得到的结果是
false
2.将构造函数内部的方法,放到构造函数的原型对象上,只为该方法开辟一次内存空间,可以复用该方法,从而避免了内存浪费。
function Person(name, age) {this.name = name;this.age = age;
}// 将构造函数的方法定义到原型对象上
Person.prototype.sayHi = function () {console.log("hi");
};var teacher = new Person("Jackson", 40);
var student = new Person("Bill", 18);
console.log(teacher.sayHi === student.sayHi); // true
解析:由于把 sayHi 方法定义到prototype对象上,那么每new一次对象实例时,该实例的方法都指向prototype对象的sayHi 方法上(同一块内存空间),因此这两个实例方法指向的地址是相同的,对比后得到的结果是
true
2.4.2 __proto__ 属性
__proto__:每个对象都有一个 __proto__属性,它指向构造函数的prototype原型对象,之所以对象可以使用prototype对象的属性和方法,就是因为 __proto__的存在。
1. __proto__指向构造函数的prototype原型对象
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHi = function () {console.log("hi");
};var student = new Person("Bill", 18);
console.log(student.__proto__);
运行结果如下:可以看到__proto__拥有sayHi方法,而这个方法是定义在prototype对象上的,也就是说__proto__指向了prototype原型对象
注:此处的
[[Prototype]]可以粗略的理解为就是__proto__

2.__proto__指向了prototype,因此这两者其实是等价的
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHi = function () {console.log("hi");
};var student = new Person("Bill", 18);
console.log(student.__proto__ === Person.prototype); // true
2.4.3 constructor属性
constructor:__proto__和 prototype里面都有一个constructor属性,constructor称为构造函数,因为它指回构造函数本身。
constructor用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
1.constructor指向原型对象引用的构造函数
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHi = function () {console.log("hi");
};var student = new Person("Bill", 18);
console.log(Person.prototype.constructor);
console.log(student.__proto__.constructor);
运行结果如下:

2.可以利用 constructor属性,将原型对象指回原来的构造函数
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype = {// 使用constructor属性,将原型对象指回Person构造函数constructor: Person,sayHi: function () {console.log("Hi");},sayHello: function () {console.log("Hello");}
};var student= new Person("Bill", 18);
student.sayHi();
student.sayHello();
注:如果我们给原型对象赋值的是一个对象的情况下,原型对象就失去了constructor属性,这时候就需要手动将constructor属性添加回来,并指回原来的构造函数。
2.4.4 原型链
原型链:JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
- 先捋清楚构造函数、实例和原型对象三者之间的关系,再理解原型链
- 原型链是通过
__proto__层层向上查找原型对象prototype,直到查找到顶层原型对象Object.prototype - 每一层原型链中,存在着构造函数、实例和原型对象三者之间的关系
通过如下例子,分析构造函数、实例和原型对象三者之间的关系,以及原型链:
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHi = function () {console.log("hi");
};var student = new Person("Bill", 18);
console.log(student.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
分析:
1.student.__proto__指向Person.prototype
2.Person.prototype.__proto__指向Object.prototype
3.Object.prototype是顶层原型对象,因此Object.prototype.__proto__指向null
1.构造函数、实例和原型对象三者之间的关系

2.原型链

2.5 Object对象
2.5.1 管理对象
管理对象的方法:
| 方法 | 描述 |
|---|---|
create() | 以现有对象为原型创建新对象 |
defineProperty() | 添加或更改对象的单个属性 |
defineProperties() | 添加或更改对象属性的多个属性 |
getOwnPropertyDescriptor() | 获取对象单个属性的描述符 |
getOwnPropertyDescriptors() | 获取对象所有属性的描述符 |
getOwnPropertyNames() | 以数组返回所有属性 |
getPrototypeOf() | 获取对象的原型 |
keys() | 以数组返回可枚举属性 |
create方法,以现有对象为原型创建新对象
var person = {name: "Bill",age: 18,sayHi: function () {console.log("Hi");}
};// 以person对象为原型,创建一个新对象student
var student = Object.create(person);
student.age = 28;console.log(student.name); // Bill
console.log(student.age); // 28
student.sayHi(); // Hi
分析:
student.name和student.sayHi()可以使用,是因为person对象是student对象的原型,student对象继承了person对象的属性和方法,而student.age为28是因为student对象覆盖了person对象的 age 属性
2.defineProperty方法,用于添加或更改对象属性
Object.defineProperty(obj, prop, descriptor):
obj:要定义属性的对象prop:要定义或修改的属性的名称descriptor:要定义或修改的属性描述符
属性描述符分为数据描述符和存取描述符:
- 数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的
- 存取描述符是由 getter 函数和 setter 函数所描述的属性
- 数据描述符和存取描述符不能混合使用
| 数据描述符 | 描述 |
|---|---|
value | 属性值 |
writable | 属性值是否可更改 |
enumerable | 属性是否可枚举 |
configurable | 属性是否可以被删除 |
| 存取描述符 | 描述 |
|---|---|
get() | 当访问该属性时,会调用此函数 |
set() | 当属性值被修改时,会调用此函数 |
数据描述符:
var person = {name: "Bill",age: 18,addr: "Shanghai"
};// 设置name属性为只读
Object.defineProperty(person, "name", { writable: false });
person.name = "Jackson";
console.log(person.name); // Bill// 设置age属性为不可枚举
Object.defineProperty(person, "age", { enumerable: false });
for (var i in person) {console.log(i); // name addr
}// 设置addr属性为不可删除
Object.defineProperty(person, "addr", { configurable: false });
delete person.addr;
console.log(person.addr); // Shanghai
存取描述符:
var person = {name: "Bill",age: 18,addr: "Shanghai"
};// 添加getter和setter
Object.defineProperty(person, "uage", {get() {return this.age;},set(value) {this.age = value;}
});// 访问getter
console.log(person.uage); // 18// 访问setter
person.uage = 28;
console.log(person.uage); // 28
console.log(person.age); // 28
3.defineProperties方法,用于添加或更改对象属性,可同时操作对象的多个属性
Object.defineProperties(obj, props):
- 要定义属性的对象
- 要定义其可枚举属性或修改的属性描述符的对象
数据描述符:
var person = {name: "Bill",age: 18,addr: "Shanghai"
};// 同时定义person对象的多个属性
Object.defineProperties(person, {name: {writable: false},age: {enumerable: false},addr: {configurable: false}
});// name属性只读
person.name = "Jackson";
console.log(person.name); // Bill// age属性不可枚举
for (var i in person) {console.log(i); // name addr
}// addr属性不可删除
delete person.addr;
console.log(person.addr); // Shanghai
存取描述符:
var person = {name: "Bill",age: 18,addr: "Shanghai"
};// 同时定义person对象的多个属性
Object.defineProperties(person, {uname: {get() {return this.name;},set(value) {this.name = value;}},uage: {get() {return this.age;},set(value) {this.age = value;}}
});// 访问uname属性
console.log(person.uname); // Bill
person.uname = "Jackson";
console.log(person.uname); // Jackson// 访问uage属性
console.log(person.uage); // 18
person.uage = 28;
console.log(person.uage); // 28
4.getOwnPropertyDescriptor 和 getOwnPropertyDescriptors方法,用于获取对象属性的描述符
获取对象的单个属性的描述符:getOwnPropertyDescriptor(obj, prop)
var person = {name: "Bill",age: 18,addr: "Shanghai"
};var descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor); // {value: 'Bill', writable: true, enumerable: true, configurable: true}
获取对象所有属性的描述符:getOwnPropertyDescriptors(obj)
var person = {name: "Bill",age: 18,addr: "Shanghai"
};var descriptor = Object.getOwnPropertyDescriptors(person);
console.log(descriptor.name); // {value: 'Bill', writable: true, enumerable: true, configurable: true}
console.log(descriptor.age); // {value: 18, writable: true, enumerable: true, configurable: true}
console.log(descriptor.addr); // {value: 'Shanghai', writable: true, enumerable: true, configurable: true}
5.getOwnPropertyNames方法,以数组返回所有属性的属性名
var person = {name: "Bill",age: 18,addr: "Shanghai"
};var properties = Object.getOwnPropertyNames(person);
console.log(properties); // ['name', 'age', 'addr']
6.getPrototypeOf方法,用于访问对象的原型
var person = {name: "Bill",age: 18,sayHi: function () {console.log("Hi");}
};var student = Object.create(person); // 以person对象为原型,创建一个新对象student
var prototype = Object.getPrototypeOf(student); // 获取student的原型
console.log(prototype); // {name: 'Bill', age: 18, sayHi: ƒ}// student的原型就是person对象,而person的原型是Object的原型
console.log(prototype == person); // true
console.log(Object.getPrototypeOf(person) == Object.prototype); // true
7.keys方法,以数组返回可枚举属性
var person = {name: "Bill",age: 18,addr: "Shanghai"
};Object.defineProperty(person, "age", { enumerable: false }); // 设置age属性为不可枚举
console.log(Object.keys(person)); // ['name', 'addr']
2.5.2 保护对象
保护对象的方法:
| 方法 | 描述 |
|---|---|
preventExtensions() | 防止向对象添加属性 |
isExtensible() | 如果对象可扩展,则返回 true |
seal() | 防止添加和删除属性 |
isSealed() | 如果对象被密封,则返回 true |
freeze() | 防止向对象进行任何更改 |
isFrozen() | 如果对象被冻结,则返回 true |
总结:以上
preventExtensions、seal、freeze三个方法对于对象的限制层层递进。
1.preventExtensions:对象不可添加属性,但是可以修改和删除属性
2.seal:对象不可添加和删除属性,但是可以修改属性
3.freeze:对象不可添加、删除和修改属性
1.preventExtensions方法, 让一个对象变的不可扩展,防止向对象添加属性
var person = {name: "Bill",age: 18
};Object.preventExtensions(person); // 防止对象添加属性
Object.defineProperty(person, "addr", { value: "Shanghai" }); // Uncaught TypeError
2.isExtensible方法,如果对象可扩展,则返回 true
var person = {name: "Bill",age: 18
};Object.preventExtensions(person);
console.log(Object.isExtensible(person)); // false
3.seal方法, 封闭一个对象,阻止添加新属性,并且不允许删除现有属性
var person = {name: "Bill",age: 18
};Object.seal(person); // 防止对象添加和删除属性
person.addr = "Shanghai";
delete person.age;
console.log(person); // {name: 'Bill', age: 18}
4.isSealed方法,如果对象被密封,则返回 true
var person = {name: "Bill",age: 18
};Object.seal(person);
console.log(Object.isSealed(person)); // true
5.freeze方法,冻结一个对象,即对象无法添加、删除和修改属性
var person = {name: "Bill",age: 18
};Object.freeze(person); // 防止对象添加、删除和修改属性
person.addr = "Shanghai";
delete person.age;
person.name = "Jackson";
console.log(person); // {name: 'Bill', age: 18}
6.isFrozen方法,如果对象被冻结,则返回 true
var person = {name: "Bill",age: 18
};Object.freeze(person);
console.log(Object.isFrozen(person)); // true
3. 函数进阶
3.1 函数的定义和使用
1.普通函数的声明与调用
// 函数声明
function getSum(x, y) {return x + y;
}getSum(1, 2); // 函数调用
2.函数表达式
1.函数可以使用表达式来定义
2.使用表达式定义的函数是匿名函数
// 函数声明
var sum = function (x, y) {return x + y;
};sum(1, 2); // 函数调用
3.函数提升
1.提升:将声明移动到当前作用域顶端的默认行为
2.使用函数表达式定义的函数不会被提升
// 函数调用
getSum(1, 2);// 函数声明
function getSum(x, y) {return x + y;
}
分析:因为函数的声明提升到作用域顶端,实际上函数调用还是在函数声明之后,因此不会报错。
4.自调用函数
- 函数表达式可以作为“自调用”
- 表达式后面跟着
(),那么函数表达式会自动执行 - 无法对函数声明进行自调用
// 函数自调用
(function () {console.log("Hello World");
})();
var sum = (function (x, y) {return x + y;
})(1, 2);console.log(sum); // 3
5.函数是对象
函数是
function对象,因此函数拥有属性和方法
function getSum(x, y) {return x + y;
}console.log(typeof getSum); // function
3.2 Function对象
Function对象:每个 js 函数都是一个 Function 对象。
Function对象的方法:
call():会调用函数,并改变函数内部的this指向apply():会调用函数,并改变函数内部的this指向bind():不会调用函数,可以改变函数内部的this指向
1.call、apply 和 bind 三个方法都可以改变函数内部的this指向
2.call 和 apply 方法会调用函数,bind 方法不会调用函数
3.call 和 apply 传递的参数不一样,call 传递参数的方式是arg1,arg2,而 apply 必须是数组形式[args]
3.2.1 call
语法:function.call(thisArg, arg1, arg2, ...)
function:函数名thisArg:this指向的对象,可选arg1, arg2, ...:函数的参数列表
1.使用call方法调用函数,可以改变函数内部的this指向
var person = {name: "Bill"
};function fn() {console.log(this);
}fn(); // Window {window: Window, ...}
fn.call(person); // {name: 'Bill'}
解析:
1.直接调用函数,this指向window对象
2.使用call方法,将this指向person对象
2.call方法可以传入参数,并获取函数的返回值
var person = {name: "Bill"
};function fn(a, b) {console.log(this);return a + b;
}var sum = fn.call(person, 1, 2); // {name: 'Bill'}
console.log(sum); // 3
3.call方法的主要应用是实现继承
例:子构造函数继承了父构造函数的属性
// 父构造函数
function Father(name, age) {// this指向父构造函数的对象实例this.name = name;this.age = age;
}// 子构造函数
function Son(name, age) {// this指向子构造函数的对象实例Father.call(this, name, age); // 修改父构造函数的this为子构造函数的this
}var son = new Son("Bill", 18);
console.log(son); // Son {name: 'Bill', age: 18}
3.2.2 apply
语法:function.apply(thisArg, argsArray)
function:函数名thisArg:this指向的对象,可选argsArray:数组形式的函数参数列表
1.使用apply方法调用函数,可以改变函数内部的this指向
var person = {name: "Bill"
};function fn() {console.log(this);
}fn.apply(person); // {name: 'Bill'}
2.apply方法可以传入参数(必须是数组形式),并获取函数的返回值
var person = {name: "Bill"
};function fn(a, b) {console.log(this);return a + b;
}var sum = fn.apply(person, [1, 2]); // {name: 'Bill'}
console.log(sum); // 3
3.apply方法的主要应用是操作数组
例:利用apply借助Math对象求最大值
var arr = [1, 3, 2];// Math.max(1, 3, 2)
max = Math.max.apply(null, arr); // 通过apply将arr数组传递给了max方法
console.log(max); // 3
解析:
1.null表示不改变this指向
2.arr数组的内容作为了参数列表,传递给了max方法
3.2.3 bind
语法:function.bind(thisArg, arg1, arg2, ...)
function:函数名thisArg:this指向的对象,可选arg1, arg2, ...:函数的参数列表
1.使用bind方法会改变函数内部的this指向,但是不会调用函数
注:bind方法的返回值,是原函数改变this之后产生的新函数
var person = {name: "Bill"
};function fn() {console.log(this);
}var f = fn.bind(person);
f(); // {name: 'Bill'}
2.bind方法可以传入参数,并获取函数的返回值(新函数)
var person = {name: "Bill"
};function fn(a, b) {console.log(this);return a + b;
}var f = fn.bind(person, 1, 2); // f是返回的新函数
var sum = f(); // sum是f函数的返回值
console.log(sum); // 3
3.如果有的函数不需要立即调用,但是又想改变这个函数内部的this指向时,使用bind方法
例:页面上有一个按钮,当点击按钮之后,就禁用这个按钮,3秒钟之后再开启这个按钮
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><button>点击一下</button><script>var btn = document.querySelector("button");btn.onclick = function () {this.disabled = true; // 此处this指向的是btnsetTimeout(function () {this.disabled = false; // 该回调函数绑定外部的this之后,此处的this也指向btn,否则指向window}.bind(this),3000);};</script></body>
</html>
3.3 高阶函数
高阶函数:对其它函数进行操作的函数,称为高阶函数,它接收函数作为参数或将函数作为返回值。
1.函数作为参数传递
function fn(a, b, callback) {console.log(a + b);callback && callback(); // 调用回调函数
}// 匿名函数作为参数传递
fn(1, 2, function () {console.log("Hello World");
});
2.函数作为返回值
function fn(a, b) {console.log(a + b);// 将匿名函数作为返回值return function () {console.log("Hello World");};
}f = fn(1, 2);
f();
3.4 闭包
闭包:指有权访问另一个函数作用域中的变量的函数。
- 一个作用域可以访问另一个函数内部的局部变量
- 闭包是一个函数
- 定义变量所在的函数,为闭包函数
1.闭包的产生
// fn为闭包
function fn() {var num = 10;function fun() {console.log(num); // 10}fun();
}fn();
解析:
1.在fun函数作用域内,访问了另外一个函数fn的局部变量num,因此产生了闭包
2.num在fn函数内定义,因此fn为闭包
在产生闭包的地方打一个断点,可以看到右侧多了一个Closure (fn),代表fn是闭包。

2.闭包的主要作用:延伸了变量的作用范围
// fn为闭包
function fn() {var num = 10;return function () {console.log(num);};
}var f = fn();
f(); // 10
解析:
1.在未没有闭包的情况下,局部变量num会随着函数fn调用结束,而随之销毁
2.有了闭包后,函数fn调用结束后,num并未销毁,而是等待函数f调用,使用了变量num后再销毁,从而延伸了局部变量num的作用范围
4. 异常
异常:执行代码的时候,可能会发生各种错误,发生错误的时候会抛异常。当抛异常后,程序会中断,不再执行后续代码,有时我们希望后续代码继续执行,这时候就需要使用处理异常的语句。
异常语句:
try语句能够检测代码块中的错误catch语句允许你处理错误throw语句允许你创建自定义错误finally表示无论 try 和 catch 结果如何,都会执行的代码
1.不使用异常语句的情况下,当代码发生错误,程序会中断,后续代码不再执行
var x = y + 1; // ReferenceError
console.log("Hello World");
运行结果如下:可以发现,错误发生后,后续的输出语句并未执行

2.try...catch 语句
语法:先执行try中的代码块,如果出现错误,接着执行catch中的代码块,否则直接执行后续代码
try {// 用于检测错误的代码块
}catch(err) {// 错误出现后执行的代码块
}
try {var x = y + 1; // ReferenceError
} catch (error) {console.log(error); // 打印错误信息
}console.log("Hello World");
此处的
error是当错误发生时提供错误信息的内置error对象,error对象还拥有两个属性:
1.name:设置或返回错误名
2.message:设置或返回错误消息
运行结果如下:可以发现,发生错误后,执行了catch语句,然后再执行后续代码,程序未发生中断

3.try...catch...finally 语句
语法:与try...catch 语句不同之处在于,无论如何都会执行finally中的代码块
try {// 用于检测错误的代码块
}catch(err) {// 错误出现后执行的代码块
}
finally {// 无论结果如何都执行的代码块
}
try {var x = y + 1;
} catch (error) {console.log(error);
} finally {console.log("Hi");
}console.log("Hello World");
4.throw 语句,用于创建自定义错误,当配合 try...catch 一起使用,就可以控制程序流并生成自定义错误消息
// 本例规定数字在5-10的范围,为有效数字
try {var x = prompt("请输入一个数字");if (x < 5) throw "太小";if (x > 10) throw "太大";alert("输入的数字有效");
} catch (error) {alert("输入的数字" + error);
}
5.js的六种错误类型
| 错误类型 | 描述 |
|---|---|
EvalError | 已在 eval() 函数中发生的错误 |
RangeError | 已发生超出数字范围的错误 |
ReferenceError | 已发生非法引用 |
SyntaxError | 已发生语法错误 |
TypeError | 已发生类型错误 |
URIError | 在 encodeURI() 中已发生的错误 |
5. JSON
JSON:JavaScript 对象标记法(JavaScript Object Notation),是一种存储和交换数据的语法。
- json是一种语法,是一种书写文本的格式
- 可以将js中的json的概念一分为二的理解,一个是json对象,另一个是json字符串
- json对象和json字符串之间可以相互转换
json对象:json格式的对象
json字符串:json格式的字符串
5.1 JSON语法
JSON 语法:
- 数据为键值对
- 数据由逗号分隔
- 花括号容纳对象
- 方括号容纳数组
注:
1.JSON 文件的后缀是.json
2.JSON 文本的 MIME 类型是application/json
1.JSON的数据是以键值对的形式存储
- 键必须是字符串,且由双引号包围
- 值只能是字符串、数字、json对象、数组、布尔和null,其中字符串必须由双引号包围
注:JSON不允许有注释
{ "name": "Bill", "age": 18 }
2.JSON中的对象和数组
JSON中的对象和数组,可以相互嵌套
{"name": "Bill","age": 18,"cars": {"car1": "Porsche","car2": "BMW","car3": "Volvo"},"models": ["Cayenne", "X5", "XC60"]
}
{"name": "Bill","age": 18,"cars": [{ "name": "Porsche", "models": ["Cayenne", "Panamera"] },{ "name": "BMW", "models": ["X5", "i3", "530Li"] },{ "name": "Volvo", "models": ["XC60", "S60"] }]
}
3.JS中的JSON对象
JSON对象的书写和JSON文本的书写,不同的地方在于JSON对象的键没有双引号包围,这是因为JSON对象是JS对象,而JS对象的键没有双引号包围。
// JSON对象
var json = {name: "Bill",age: 18,cars: [{ name: "Porsche", models: ["Cayenne", "Panamera"] },{ name: "BMW", models: ["X5", "i3", "530Li"] },{ name: "Volvo", models: ["XC60", "S60"] }]
};// 使用objectName.property的语法访问对象属性
console.log(json.name); // Bill
console.log(json.age); // 18
console.log(json.cars[0]); // {name: 'Porsche', models: Array(2)}
console.log(json.cars[0].name); // Porsche
console.log(json.cars[0].models[0]); // Cayenne// 使用objectName["property"]的语法访问对象属性
console.log(json["name"]); // Bill
console.log(json["age"]); // 18
console.log(json["cars"][1]); // {name: 'BMW', models: Array(3)}
console.log(json["cars"][1]["name"]); // BMW
console.log(json["cars"][1]["models"][0]); // X5
5.2 JSON解析
JSON解析:使用JSON.parse()方法,将json字符串转换为json对象
var jsonstr = '{"name":"Bill","age":18,"cars":{"car1":"Porsche","car2":"BMW","car3":"Volvo"}}';
var json = JSON.parse(jsonstr);
console.log(json); // {name: 'Bill', age: 18, cars: {…}}
还可以使用ES6中的模板字符串,保留JSON文本原有的写法:
// JSON字符串
var jsonstr = `{"name": "Bill","age": 18,"cars": {"car1": "Porsche","car2": "BMW","car3": "Volvo"}
}`;var json = JSON.parse(jsonstr);
console.log(json); // {name: 'Bill', age: 18, cars: {…}}
5.3 JSON字符串化
JSON字符串化:使用JSON.stringify()方法,将json对象转换为json字符串
// JSON对象
var json = {name: "Bill",age: 18,cars: {car1: "Porsche",car2: "BMW",car3: "Volvo"}
};// 将JSON对象转换为字符串
var jsonstr1 = JSON.stringify(json);
console.log(jsonstr1);// 将JSON对象转换为字符串,并指定缩进用于美化输出
var jsonstr2 = JSON.stringify(json, null, "\t");
console.log(jsonstr2);
运行结果如下:

6. 拷贝
拷贝分为浅拷贝和深拷贝:
- 浅拷贝:只拷贝第一层的数据,当碰到更深层次的对象时,则只拷贝对象的地址
- 深拷贝:拷贝每一层的数据
6.1 浅拷贝
1.使用Object.assign()方法,可以实现浅拷贝
var person = {name: "Bill",age: 18,info: {telephone: "13579",email: "13579@163.com"}
};var student = {};
Object.assign(student, person); // 将person对象浅拷贝到student对象
person.age = 28;
person.info.telephone = "246810";
console.log(person);
console.log(student);
运行结果如下:发现person.age的修改没有影响到student对象,而person.info.telephone的修改却影响到了student对象。
分析:这是因为
person.age的属性值是简单数据类型,因此拷贝到student对象中的age属性值是一个具体的值,而person.info的属性值是一个对象,因此拷贝到student对象中的info的属性值是一个地址,这个地址指向了person.info。也就是说student.info === person.info,因此person.age的修改不影响student,而person.info.telephone的修改会影响student

2.直接将对象赋值给另一个对象的话,只是相当于给对象取了个别名,既不是浅拷贝也不是深拷贝
var person = {name: "Bill",age: 18,info: {telephone: "13579",email: "13579@163.com"}
};var student = {};
student = person; // 将person对象的地址赋值给student对象
person.age = 28;
person.info.telephone = "246810";
console.log(person);
console.log(student);
运行结果如下:person对象的任何属性发生改变,student对象的属性也跟着改变,因为student指向的就是person对象的地址,即 student === person

6.2 深拷贝
1.使用structuredClone()方法,可以实现深拷贝
var person = {name: "Bill",age: 18,info: {telephone: "13579",email: "13579@163.com"}
};var student = structuredClone(person); // 将person对象深拷贝到student对象
person.age = 28;
person.info.telephone = "246810";
console.log(person);
console.log(student);
运行结果如下:发现person.age和person.info.telephone的修改,没有影响到student对象。
分析:这是因为深拷贝会拷贝每一层的数据,当碰到更深层次的对象时,会继续遍历对象的属性和属性值并拷贝,而不是只拷贝对象的地址。

2.使用for...in封装一个深拷贝函数
// 深拷贝函数
function deepCopy(newObj, oldObj) {for (var i in oldObj) {var item = oldObj[i]; // 原对象的属性值// 判断属性值是否为数组对象if (item instanceof Array) {newObj[i] = []; // 将新对象的第i项属性设置为数组对象deepCopy(newObj[i], item); // 递归调用}// 判断属性值是否为普通对象if (item instanceof Object) {newObj[i] = {}; // 将新对象的第i项属性设置为普通对象deepCopy(newObj[i], item); // 递归调用}// 判断属性值是否为简单数据类型if (!(item instanceof Object)) {newObj[i] = item;}}
}var person = {name: "Bill",age: 18,info: {telephone: "13579",email: "13579@163.com"},addr: ["Beijing", "Shanghai"]
};var student = {};
deepCopy(student, person); // 将person对象深拷贝到student对象
person.age = 28;
person.info.telephone = "246810";
person.addr[0] = "Guangzhou";
console.log(person);
console.log(student);
运行结果如下:

3.使用JSON封装一个深拷贝函数
// 深拷贝函数
function deepCopy(oldObj) {oldObjstr = JSON.stringify(oldObj); // 将原对象转换为字符串newObj = JSON.parse(oldObjstr); // 再将字符串转换为对象return newObj;
}var person = {name: "Bill",age: 18,info: {telephone: "13579",email: "13579@163.com"},addr: ["Beijing", "Shanghai"]
};var student = deepCopy(person); // 将person对象深拷贝到student对象
person.age = 28;
person.info.telephone = "246810";
person.addr[0] = "Guangzhou";
console.log(person);
console.log(student);
运行结果:

7. ES6
参考后续ES6教程
相关文章:
JS高级知识总结
文章目录1. this指向问题2. 对象进阶2.1 对象的定义和使用2.2 对象访问器2.2.1 Getter2.2.2 Setter2.3 对象构造器2.4 对象原型2.4.1 prototype属性2.4.2 \_\_proto\_\_ 属性2.4.3 constructor属性2.4.4 原型链2.5 Object对象2.5.1 管理对象2.5.2 保护对象3. 函数进阶3.1 函数的…...
Jenkins+Docker+Maven+gitlab实现自动构建、远程发布
前言 一个项目完整的生命周期是从开发的coding阶段和coding阶段的质量测试,再到多次发布投入使用。目前大部分的测试阶段并不是从coding结束后开始的,而是和coding同步进行的。可能今天早上coding完成一个功能,下午就要投入测试。在这期间&a…...
centos7克隆虚拟机完成后的的一些配置介绍
系列文章目录 centos7配置静态网络常见问题归纳_张小鱼༒的博客-CSDN博客 文章目录 目录 系列文章目录 前言 一、配置Hadoop要下载的压缩包 1、下载对应版本的Hadoop压缩包 2、我们如何查看自己电脑的端口号 3、下载jdk对应的版本 二、虚拟机centos7克隆虚拟机完成后的一些基本…...
C语言/动态内存管理函数
C程序运行时,内存将被划分为三个区域,而动态开辟的内存区间位于堆区。 文章目录 前言 一、内存划分 二、malloc函数 三、calloc函数 四、realloc函数 五、free函数 总结 前言 在使用C语言编写程序时,使用动态内存是不可避免的&#x…...
华为OD机试题,用 Java 解【任务调度】问题
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不要…...
河南农业大学2023春蓝桥杯赛前训练第一场
A 滑板上楼梯 贪心 要求最少次数,尽量多跳三阶的,不能连续跳三阶,三阶后面一定要跟着一个一阶,相当于直接跳四阶 每次跳四阶都是两步(3、1),如果 % 4 之后,正好剩下 3 ,…...
docker-dockerfile
1.常用保留字指令 FROM : 基础镜像MAINTAINER: 维护者姓名和邮箱RUN : Run ["可执行文件",参数1]; Run [shell命令]EXPOSE: 暴露出的端口号WORKDIR: 登录后的位置USER: 执行用户,默认是rootENV: 构建过程的环境变量ADD: 将宿主机的文件拷贝到…...
【JavaEE】浅识进程
一、什么是进程1.1 操作系统学习进程之前首先要了解我们的操作系统(OS),我们的操作系统实际上也是一款软件,属于系统软件的范畴,操作系统早期采用命令提示框与用户交互,我们启动某个软件,打开某…...
Java_Spring:1. Spring 概述
目录 1 spring 是什么 2 Spring 的发展历程 3 spring 的优势 4 spring 的体系结构 1 spring 是什么 Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspec…...
使用Maven实现第一个Servlet程序
目录 前言: Maven 什么是Maven 创建Maven项目 Mevan目录介绍 Servlet程序 引入Servlet依赖 创建目录结构 编写代码 打包程序 部署程序 验证程序 idea集成Tomcat 下载Tomcat插件 配置Tomcat的路径 Smart Tomcat工作原理 小结: 前言&#…...
【MySQL】MySQL的优化(一)
目录 查看SQL执行频率 定位低效率执行SQL 定位低效率执行SQL-慢查询日志 定位低效率执行SQL-show processlist 查看SQL执行频率 MySQL 客户端连接成功后,通过 show [session|global] status 命令可以查看服务器状态信息。通 过查看状态信息可以查看对当…...
win kubernetes dashbord部署springboot服务
文章目录前言一、新建springboot工程二、制作镜像1.编写dockerfile2.使用阿里云镜像仓库3.使用dashbord部署服务总结前言 使用win版docker desktop安装的k8s,kubenetes dashbord。 一、新建springboot工程 就是简单一个接口。没什么说的 二、制作镜像 1.编写dock…...
Linux之进程终止
本节目录1.进程终止2.exit与_exit函数1.进程终止 进程终止时,操作系统做了什么? 释放进程中申请的相关内核数据结构和对应的数据和代码。本质就是释放系统资源。 进程终止的常见方式 a.代码跑完,结果正确 b.代码跑完,结果不正确…...
全网独家首发|极致版YOLOv7改进大提升(推荐)网络配置文件仅24层!更清晰更方便更快的改进YOLOv7网络模型
有不少小伙伴和我交流YOLO改进的时候,都说YOLOv7的网络配置文件长达104层,改起来很费力,数层数都要数很久,还很容易出错,而且基于YOLOv5代码架构,Debug起来也确实比较费时,所以博主对YOLOv7网络…...
C++入门 谁都能看懂的类和对象
类 C语言结构体中只能定义变量. 在C中,结构体内不仅可以定义变量,也可以定义函数。 //c语言 typedef struct ListNode {int val;struct ListNode* next; }LTN; //c struct ListNode {int val;//c中可以直接用这个,不用加structListNode* next…...
C++ STL:string类的模拟实现
目录 前置说明 一. 构造函数和析构函数的模拟实现 1.1 构造函数 1.2 析构函数 二. string类对象容量及成员相关的函数 2.1 获取字符串有效字符数、容量及_str成员变量获取相关函数 2.2 扩容及变长相关函数 2.3 字符串清空和判空函数 三. 运算符重载函数 3.1 赋值运算…...
并发编程---线程池(六)
阻塞队列的应⽤——线程池一 线程池基本概念二 线程池三种常⽤创建⽅式2.1.newFixedThreadPool线程池:2.2.newSingleThreadExecutor线程池:2.3.newCachedThreadPool线程池:2.4. 线程池代码演示三 线程池创建的七个参数四 线程池底层原理理解&…...
【Java实战】不会还有人用if else进行参数校验吧
当请求参数很多,几乎每一个参数都需要后端去兜底校验时,你还在写if else去判断参数是否为空吗??要校验为空的参数三四个还好,要是十几个,业务逻辑还没开始就写二三十行代码开始堆山了嘛,教给大家…...
深度学习部署(十六): CUDA RunTime API _vector-add 使用cuda核函数实现向量加法
1. 知识点 nthreads的取值,不能大于block能取值的最大值。一般可以直接给512、256,性能就是比较不错的 (input_size block_size - 1) / block_size;是向上取整 对于一维数组时,采用只定义layout的x维度,若处理的是二维ÿ…...
堆结构的两个应用
堆排序 堆结构很大的一个用处,就是用于堆排序了,堆排序的时间复杂度是O(n∗log2n)O(n*log_2n)O(n∗log2n)量级的,在众多排序算法中所处的地位也是高手级别的了。 但很多人在使用堆排序的时候,首先认为我必须得有一个堆数据结构…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
