西安网站建设制作价格低,西瓜创客少儿编程加盟,青海住房建设厅网站,wordpress5.2.2怎么改中文文章目录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 函数的…
文章目录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 函数的定义和使用3.2 Function对象3.2.1 call3.2.2 apply3.2.3 bind3.3 高阶函数3.4 闭包4. 异常5. JSON5.1 JSON语法5.2 JSON解析5.3 JSON字符串化6. 拷贝6.1 浅拷贝6.2 深拷贝7. ES61. 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 langenheadmeta charsetUTF-8 /meta http-equivX-UA-Compatible contentIEedge /meta nameviewport contentwidthdevice-width, initial-scale1.0 /titleDocument/title/headbodybutton点击一下/buttonscriptvar 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()如果对象可扩展则返回 trueseal()防止添加和删除属性isSealed()如果对象被密封则返回 truefreeze()防止向对象进行任何更改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); // 35.函数是对象 函数是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函数名thisArgthis指向的对象可选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函数名thisArgthis指向的对象可选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函数名thisArgthis指向的对象可选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 langenheadmeta charsetUTF-8 /meta http-equivX-UA-Compatible contentIEedge /meta nameviewport contentwidthdevice-width, initial-scale1.0 /titleDocument/title/headbodybutton点击一下/buttonscriptvar 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
JSONJavaScript 对象标记法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: 13579163.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: 13579163.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: 13579163.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: 13579163.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: 13579163.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教程