JS面向对象
单例模式
//对象数据类型的作用:
//把描述同一个事物(同一个对象)的属性和方法放在一个内存空间下,起到了分组的作用,这样不同事物之间的属性即使属性名相同,相互也不会发生冲突
//->我们把这种分组编写代码的模式叫做"单例模式"
//->在单例模式中我们把person1或者person2也叫做"命名空间"
let person1 = {
name : 'xiaohong',
age : 22
}
let person2 = {
name : 'xiaoyan',
age : 22
}
console.log(person1.name);
console.log(person2.name);
//单例模式是一种项目开发中经常使用的模式,因为项目中我们可以使用单例模式来进行"模块化开发"
//"模块化开发":对于一个相对来说比较大的项目,需要多人协作的开发的,一般情况下会根据当前项目的需求划分成几个功能板块,每个人负责一部分,同时开发,最后把每个人的代码进行合并
工厂模式
//单例模式虽然解决了分组的作用,但是不能实现批量的生成,属于手工作业模式 -> 所以产生了"工厂模式"
// 把实现同一件事情的相同的代码放到一个函数中,以后如果想实现这个功能,不需要重新编写这些代码了,只需要执行这个函数即可 -> "函数的封装" -->"低耦合高内聚":减少页面中的冗余代码,提高代码的重复利用率
function person(name,age) {
let obj = {}
obj.name = name
obj.age = age
obj.hello = function(){
console.log(`my name is ${this.name}`)
}
return obj
}
let xiaohong = person('小红',22)
let xiaoyan = person('小燕',22)
xiaohong.hello()
xiaoyan.hello()
构造函数
//构造函数模式和工厂模式的区别
//1、执行的时候
// 普通函数执行 ->person() let xiaohong = person('小红',22)
// 构造函数模式 -> new Person() 通过new执行后, Person就是一个类了 let xiaohong = new Person()
//而函数执行的返回值(xiaohong)就是Person 这个类的一个实例
function Person(name,age){
//JS默认创建的对象就是我们的实例 xiaohong -> this 就是 xiaohong
this.name = name //->xiaohong.name = name
this.age = age
this.hello = function(){
console.log(`my name is ${this.name} `)
}
//JS默认返回实例 相当于 return xiaohong
}
let xiaohong = new Person('小红',22) //Person -> this 是xiaohong
xiaohong.hello() // hello方法里面的this -> xiaohong
let res = Person('老明',32) //这样写不是构战函数模式执行,而是普通的函数执行 由于没有写return,所以 res = undefined 并且 Person 这个函数中的this是winodw
let xiaoyan = new Person('小燕',22)
xiaoyan.hello()
//2、在函数代码执行的时候
//相同点:都是形成一个私有的作用域,然后 形参赋值 -> 预解释 -> 代码从上到下执行(类和普通函数一样,它也有普通函数的一面)
//不同:在代码执行之前,不用自己手动的创建对象了,JS会默认创建一个对象数据类型的值(这个对象其实就是我们当前类的一个实例)
//接下来代码从上到下执行,以当前的实例为执行的主体(this代表的就是当前的实例),然后分别的把属性名和属性值赋值给当前的实例
//最后JS会默认的把创建的实例返回
//创建一个数组
// let arr = [] //字面量方式
//let arr = new Array() //实例创建的方式 -> 构造函数模式执行的方式
//不管哪一种方式arr都是Array这个类的一个实例
//1、
//JS中所有的类都是函数数据类型的,它通过new执行变成了一个类,但是它本身也是一个普通的函数
//JS中所有的实例都是对象数据类型的
//2、
//在构造函数模式中,类中(函数体中)出现的this.xxx = xxx 中的this是当前类的一个实例
//3、
//xiaohong 和 xiaoyan 都是 Person 这个类的实例,所以都拥有hello这个方法,但是不同实例之间的方法是不一样的
//在类中给实例增加的属性(this.xxx = xxx)属于当前实例的私有属性,实例和实例之间是单独的个体,所以私有的属性之间是不相等的
console.log(xiaohong.hello === xiaoyan.hello) //->false
构造函数细节知识点
function Fn(){
this.x = 100; // this -> f1
this.getX = function(){ //this -> 需要看getX执行的时候才知道
console.log(this.x)
}
}
let f1 = new Fn;
f1.getX() //this -> f1
let ss = f1.getX;
ss() // this -> window -> undefined
//1、在构造函数模式中 new Fn() 执行,如果Fn不需要传递参数的话,后面的小括号可以省略
//2、this的问题:在类中出现的this.xxx = xxx中的this都是当前类的实例,而某一个属性值(方法),方法中的this需要看方法执行的时候,前面是否有"."才能知道this是谁
function Fn(){
var num =10
this.x = 100;
this.getX = function(){
console.log(this.x)
}
return {name:'小红'}
}
let f1 = new Fn;
console.log(f1.num)// ->undefined
console.log(f1) //{name: "小红"}
//3、类有普通函数的一面,当函数执行的时候,var num 其实只是当前形成的私有作用域中的私有变量而已,它和我们的f1这个实例没有任何的关系;只有this.xxx = xxx才相当于给f1这个实例增加私有的属性和方法,才和我们的f1有关系...
//4、在构造函数模式中,浏览器会默认的把我们的实例返回(返回的是一个对象数据类型的值);如果我们自己手动写了return返回:
//4.1、返回的是一个基本数据类型的值,当前实例是不变的,例如return 100; f1还是当前Fn类的实例
//4.2、返回的是一个引用数据类型的值,当前的实例会被自己返回的值给替换掉,例如:return {name:'小红'}; f1就不再是Fn的实例了,而是对象{name: "小红"}
function Fn() {
this.x = 100;
this.getX = function () {
console.log(this.x)
}
}
let f1 = new Fn;
// //5、检测某个实例是否属于这个类 -> instanceof
console.log(f1 instanceof Fn) //true
console.log(f1 instanceof Array) //false
console.log(f1 instanceof Object) //true 因为所有的实例都是对象数据类型的,而每一个对象数据类型都是Object这个内置类的一个实例,所以f1也是它的一个实例
//对于检测数据类型来说,typeof有自己的局限性,不能细分object下的对象、数组、正则...
function Fn() {
this.x = 100;
this.getX = function () {
console.log(this.x)
}
}
let f1 = new Fn;
let f2 = new Fn;
// 6、f1和f2都是Fn这个类的一个实例,都拥有x和getX两个属性,当时这两个属性是各自的私有的属性,所以:
console.log(f1.getX === f2.getX) //false 都是私有的属性,每个实例都会复制一份
//in: 检测某一个属性是否属于这个对象 (attr in object),不管是私有的属性,只要存在,用in来检测都是true
console.log('getX' in f1) // true 是它的一个属性
//hasOwnProperty: 用来检测某一个属性是否为这个对象的"私有属性",这个方法只能检测私有的属性
console.log(f1.hasOwnProperty('getX')) //true getX是f1的私有属性
//检测某一个属性是否为该对象的"公有属性" hasPubProperty
function hasPubProperty(obj,attr){
//首先保证是它的一个属性并且还不是私有的属性,那么只能是共有的属性了
return attr in obj && !obj.hasOwnProperty(attr)
}
console.log(hasPubProperty(f1,'getX')) // false
原型链基础
1、每一个函数数据类型(普通函数、类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值
2、并且在prototype上,JS天生给它加了一个属性constructor(构造函数),属性值是当前函数(类)本身
3、每一个对象数据类型(普通的对象、实例、prototype...)也是天生自带一个属性:"proto", 这个属性值是当前实例所属类的原型(prototype)
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.say = function(){
console.log(`hello, my name is ${this.name}`)
}
let xiaohong = new Person('小红',22)
let xiaoyan= new Person('小燕',22)
console.log(xiaohong.say === xiaoyan.say) //true
//构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的 -> 实例识别
//基于构造函数模式的原型模式解决了 方法或者属性共有的问题 -> 把实例之间相同的属性和方法提取成公有的属性和方法 -> 想让谁公有就把它放在Person(构造函数)的 prototype 上即可
//1、每一个函数数据类型(普通函数、类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值
//2、并且在prototype上,JS天生给它加了一个属性constructor(构造函数),属性值是当前函数(类)本身
//3、每一个对象数据类型(普通的对象、实例、prototype...)也是天生自带一个属性:__proto__ , 这个属性值是当前实例所属类的原型(prototype)
function Fn(){
this.x = 100
}
Fn.prototype.getX = function(){
console.log(this.x)
}
let f1 = new Fn;
let f2 = new Fn;
console.log(Fn.prototype.constructor) //ƒ Fn(){ this.x = 100 }
console.log(Fn.prototype.constructor === Fn) //true
//2、Object是JS中所有对象数据类型的基类(最顶层的类)
//2.1、 f1 instanceof Object -> true 因为f1通过 __proto__ 可以向上级查找,不管有多少级,最后总能找到Object
//2.2、在Object.prototype上没有__proto__这个属性
//3、原型链模式
// f1.hasOwnProperty('x') //->hasOwnProperty是f1的一个属性
//但是发现在f1的私有属性上并没有这个方法,那如何处理的呢?
//3.1 通过 对象名.属性名 的方式获取属性值的时候,首先在对象的私有的属性上进行查找,如果私有中存在这个属性,则获取的是私有的属性值
// 如果私有的没有,则通过__proto__找到所属类的原型(类的原型上定义的属性和方法都是当前实例的公有的属性和方法),原型上存在的话,获取的是公有的属性值
// 如果原型上也没有,则继续通过原型上的__proto__继续向上查找,一直找到Object.prototype为止...
// ->这种查找的机制就是我们的原型链模式

原型链模式扩展-批量设置公有属性
// 批量设置公有属性
//1、起一个别名
function Fn(){
this.x = 100
}
let pro = Fn.prototype //把原来原型指向的地址赋值给pro,现在它们操作的是同一个内存空间
pro.getX = function (){
}
pro.getY = function (){
}
pro.getZ = function (){
}
//2、重构原型对象的方式 -> 自己新开辟一个堆内存,存储公有的属性和方法,把浏览器原来给Fn.prototype开辟的那个替换掉
function Fn(){
this.x = 100
}
Fn.prototype = {
a: function(){
},
b:function(){
}
}
let f = new Fn;
//1、只有浏览器天生给Fn.prototype开辟的堆内存里面才有constructor,而我们自己开辟的这个堆内存没有这个属性,这样constructor指向就不再是Fn而是Object
console.log(f.constructor) //ƒ Object() { [native code] } 没有做任何处理之前 就是 Object
//为了和原来的保持一致,我们需要手动的增加constructor的指向
function Fn1(){
this.x = 100
}
Fn1.prototype = {
constructor:Fn, //一定要把constructor指回来
a: function(){
},
b:function(){
}
}
let f1 = new Fn1;
console.log(f1.constructor) //Fn(){ this.x = 100 }
//2、用这种方式给m,内置类增加公有的属性
// Array.prototype.unique = function(){}
// Array.prototype = {
// constructor : Array,
// unique:function(){
// }
// }
//我们这种方式会把之前已经存在于原型上的属性和方法给替换掉,所以我们用这种方式修改内置类的话,浏览器是给屏蔽掉的
//但是可以一个个的修改内置的方法,当我们通过下述方式在数组的原型上增加方法,如果方法名和原来内置的重复了,会把人家内置的修改掉 -> 以后再内置类的原型上增加方法,命名都需要加特殊的前缀
Array.prototype.sort = function(){
console.log('ok')
}
let arr = [3,4,1,2,5,6,3,7,8,5,9]
arr.sort() //ok
原型链模式扩展-this和原型扩展
//在原型模式中,this常用的有两种情况
//在类中this.xxx = xxx this -> 当前类的实例
//某一个方法中的this -> 看执行的时候"." 前面是谁this就是谁
//1)需要先确定this的指向(this是谁)
//2)把this替换称对应的代码
//3)按照原型链查找的机制,一步步的查找结果
function Fn(){
this.x = 100
this.y = 200
this.getY = function(){
console.log(this.y)
}
}
Fn.prototype = {
constructor : Fn,
y:300,
getX:function(){
console.log(this.x)
},
getY:function(){
console.log(this.y)
}
}
let f = new Fn;
f.getY() //->200
f.__proto__.getX() //->this是f.__proto__ ->console.log(f.__proto__.x) -> undefined
Fn.prototype.getX() //undefined
f.getY() //200
f.__proto__.getY()//300
//=================================================================>
//自定义排序
Array.prototype.myUnique = function(){
//实现数组去重
let obj = {}
for(let i = 0; i < this.length; i++){
let cur = this[i]
if(obj[cur] == cur){
this[i] = this[this.length -1]
this.length--
i--
continue
}
obj[cur] = cur
}
obj = null
return this //目的是为了实现链式写法
}
let arr = [12,34,53,64,78,35,54,13,12,34,53,64,78,35,54,13]
arr.myUnique().sort(function(a,b){
return a - b
})
console.log(arr)
// let arr = [12,34,53,64,78,35,54,13]
// arr.sort(function(a,b){
// return a -b
// }).reverse().pop()
//链式写法:执行完成数组的一个方法可以紧接着执行下一个方法
//原理:
//arr为什么可以使用sort方法? ->因为sort是Array.prototype上的公有的方法,而数组arr是Array这个类的一个实例,索引arr可以使用sort方法->数组才能使用Array原型上定义的属性和方法
//sort执行完成的返回值是一个排序后的"数组",可以继续执行reverse
//reverse执行完成的返回值是一个数组,可以继续继续pop
//pop执行完成的返回值是被删除的哪个元素,不是一个数组了 所以再使用数组方法会报错
// console.log(arr)
遍历私有属性
//遍历私有属性
Object.prototype.aaa = function(){
console.log('aaa')
}
let obj = {name:'小红',age:22}
for(let key in obj){
//-> for in 循环在变量的时候,默认的话可以把自己私有的和在它所属类原型上扩展的属性和方法都可以遍历到,但是一般情况下,遍历一个对象只需要遍历私有的即可,我们可以使用以下的判断进行处理:
// -> propertyIsEnumerable() 可枚举的
// -> hasOwnProperty() 私有的
// if(obj.propertyIsEnumerable(key)){
// console.log(key)
// }
if(obj.hasOwnProperty(key)){
console.log(key)
}
}
Object.create()
//Object.create()
//Object.create(proObj)创建一个新的对象,但是还要把proObj作为这个对象的原型
var obj = {
getX:function(){
console.log('1111')
}
}
let obj2 = Object.create(obj)
console.log(obj2)
//-> obj2
// __proto__:
// getX;function...
// __proto__:Object.prototype
obj2.getX() //1111
function objectCreate(o){ //模拟Object.create
function Fn(){}
Fn.prototype = o
return new Fn
}
let obj3 = objectCreate(obj)
obj3.getX()
function Sum(){
}
Sum.prototype = objectCreate(obj)
Sum.prototype.constructor = Sum
继承
原型继承
//原型继承
function A() {
this.x = 100
}
A.prototype.getX = function () {
console.log(this.x)
}
function B() {
this.x = 200
}
B.prototype = new A;
B.prototype.constructor = B //继承之后 construct指向有问题,需要使用constructor的话就要把它指回来
let B1 = new B;
console.log(B1.x) //200 遮蔽效果
//原型继承是我们JS中最常用的一种继承方式
// -> 子类B想要继承父类A中的所有的属性和方法(私有+公有),只需要让B.prototype = new A;即可
// -> 原型继承的特点: 它是把父类中私有的 + 公有的都继承到了子类原型上(子类公有的)
// -> 核心: 原型继承并不是把父类中的属性和方法克隆一份一模一样的给B,而是让B和A之间增加了原型链的连接,以后B的实例 B1 想要A中的 getX 方法,需要一级级的向上查找来使用

原型继承图
call继承
//call继承:把父类私有的属性和方法 克隆一份一模一样的 作为子类私有的属性
function A(){
this.x = 100
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(){
//this -> 这里的this就是实例 n
A.call(this) // A.call(n) 把A执行,让A中的this变为了n
}
let n = new B()
console.log(n.x) //100
n.getX() //n.getX is not a function 写在原型链上的方法(公有)继承不到
冒充对象继承
//冒充对象继承:把父类私有的+公有的克隆一份一模一样的给子类私有的
function A(){
this.x = 100
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(){
let temp = new A
for(let key in temp){
this[key] = temp[key]
}
temp = null
}
let n = new B()
console.log(n.x) //100
n.getX() //都是自己私有的方法了
混合模式继承
//混合模式继承:原型继承 + call继承
function A(){
this.x = 100
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(){
A.call(this) // -> n.x =100
}
B.prototype = new A
B.prototype.constructor = B //->B.prototype: x =100 getX...
let n = new B()
n.getX()
寄生组合式继承
//寄生组合式继承
function A(){
this.x = 100
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(){
A.call(this)
}
B.prototype = Object.create(A.prototype)
B.prototype.constructor = B
let n = new B()
n.getX()
函数的三种角色
function Fn(){
}
console.log(Fn.__proto__)
let f1 = new Fn
//函数本身也会有一些自己的属性:
// -> length: 0 形参的个数
// -> name : "Fn"函数名
// -> prototype 类的原型,在原型上定义的方法都是当前Fn这个类实例的公有方法
// -> __proto__ 把函数当做一个普通的对象,指向Function这个类的原型
//函数在整个JS中是最复杂也是最重要的知识:
// 1、一个函数存在了多面性
// ->"普通函数": 它本身就是一个普通的函数,执行的时候形成私有的作用域(闭包),形参赋值,预解释,代码执行,执行完成后栈内存销毁/不销毁
// ->"类": 它有自己的实例,也有一个叫做prototype属性是自己的原型,它的实例都可以指向自己的原型
// ->"普通对象": 和 var obj = {} 中的 obj 一样,就是一个普通对象,它作为对象可以有一些自己的私有的数学,也可以通过__proto__找到Function.prototype
//这三者之间是没有必然的关系的
function Fn(){
var num =5000
this.x = 100
}
Fn.prototype.getX =function(){
console.log(This.x)
}
Fn.aaa = 1000
var f = new Fn
console.log(f.num)

call用法
//Array.prototype.slice = function(){}
// let arr = [12,23,43]
//arr.slice -> arr这个实例通过原型链的查找机制找到Array.prototype上的slice方法
//arr.slice() ->让找到的slice方法执行,在执行slice方法的过程中,才把arr数组进行了截取
// Function.prototype.call = function(){}
let obj = {name:'小红'}
function fn(){
console.log(this)
}
// fn() //this -> window
// obj.fn() //obj.fn is not a function
// fn.call(obj) //{name: "小红"} this变为obj了
//call 方法的作用:
// -> 首先我们让原型上的call方法执行,在执行call方法的时候,我们让fn方法中的this变为第一个参数值obj;然后再把fn这个函数执行
//自己模拟内置的call方法,写一个myCall方法,深入探讨call方法的执行原理
Function.prototype.myCall = function(context){
//-> myCall方法中的this就是当前我要操作和改变其this关键字的那个函数名
// ->1、 让fn中的this关键字变为context的值 -> obj
// -> 让this这个函数中的"this关键字"变为context
//this.toString().replace
console.log(context)
// ->2、让fn方法执行
}
fn.myCall(obj) // ->myCall方法中的this 是fn
sum.myCall(obj)// ->myCall方法中的this 是sum
function sum(){
console.log(this)
}
//也许这就是面试题吧
function fn1(){console.log(1)}
function fn2(){console.log(2)}
fn1.call(fn2) //首先fn1通过原型链机制找到Function.prototype上的call方法,并且让call方法执行 -> 此时call这个方法中的this就是我要操作的fn1 -> 在call方法代码执行的过程中首先让fn1中的"this关键字"变为fn2,然后再让fn1这个方法执行 -> 输出1
fn1.call.call(fn2) // -> fn1.call 首先fn1通过原型链找到Function.prototype上的call方法,然后再让call方法通过原型再找到Function原型上的call(因为call本身的值也是一个函数,所以同样可以找到Function.prototype),在第二次找到call的时候让方法执行,方法中的this是fn1.call,首先让这个方法中的this变为fn2,然后再让fn1.call执行 -> 输出2
Function.prototype.call(fn1) //没有输出,因为 this 是 Function.prototype
Function.prototype.call.call(fn1) // 输出 1 Function.prototype.call执行之后 this 就变为fn1了 所以第二个call的时候是执行 fn1()
严格模式下的call
"use strict"; //严格模式
let obj = {name : '小红'}
function fn(num1,num2){
console.log(num1 + num2)
console.log(this)
}
fn(100,200) // this -> window num1=100 num2=200
fn.call(100,200) //this -> 100 num1=200 num2=undefined
fn.call(obj,100,200)//this -> obj num1=100 num2=100
fn.call() // this -> winodw 在严格模式下 this -> undefined
fn.call(null) // this -> window 在严格模式下 this -> null
fn.call(undefined) // this -> window 在严格模式下 this -> undefined
//apply 和 call方法的作用是一模一样的,都是用来改变方法的this关键字并且把方法执行;而且在严格模式下和非严格模式下对于第一个参数是null/undefined这种情况的规律也是一样的
// fn.call(obj,100,200)
// fn.apply(obj,[100,200]) // -> call在给fn传递参数的时候,是一个个的传递值的,而apply不是一个个传,而是把要给fn传递的参数值统一的放在一个数组中进行操作 -> 但是也相当于一个个的给fn的形参赋值
//bind: 和 call/apply类似都是用来改变this关键字的
//fn.call(obj,1,2) ->改变this和执行fn函数是一起都完成了
//->预处理:事先把fn的this改变为我们想要的结果,并且把对应的参数值也准备好,以后要用到了,直接的执行即可
let tempFn = fn.bind(obj,1,2) // -> 只是改变了fn中的this为obj,并且给fn传递了两个参数值1、2,但是此时并没有把fn这个函数执行;执行bind会有一个返回值,这个返回值tempFn就是我们把fn的this改变后的哪个结果
tempFn()
call,apply细节用法
//假设法:假设当前数组中的第一个值是最大值,然后拿这个值和后面的项逐一进行比较,如果后面某一个值比假设的还要大,我们把假设的值进行替换
let arr = [3,4,12,13,43,56,34,53,41,32,11]
// let max = arr[0]
// let min = arr[0]
// for(let i = 0; i <arr.length; i++){
// let cur = arr[i]
// cur > max ? max = cur :null;
// cur < min ? mix = cur :null;
// }
// console.log(max,min)
let max = Math.max.apply(null,arr) //this -> window 在里面把arr参数传给max方法,然后window.Math.max()
console.log(max)
//6、括号表达式
function fn1(){console.log(1)}
function fn2(){console.log(2)}
;(fn1,fn2)(); // -> 2 ->只有fn2执行了 ->(x1,x2,x3...)括号表达式,一个括号中出现多项内容,中间用","隔开,但是我们最后获取到的结果只有最后一项
//传入一个数组,克隆这个数组,返回一个新的数组
Array.prototype.mySlice = function(){
let arr = []
for(let i = 0; i < this.length; i++){
arr[arr.length] = this[i]
}
return arr
}
//arguments.sort() //arguments.sort is not a function -> arguments是一个类数组集合,它不是数组,不能直接使用数组的方法
function avgFn(){
//借用数组原型上的mySlice方法,当slice执行的时候,让方法中的this变为要处理的arguments,实现将类数组arguments转为数组
let arr = Array.prototype.slice.call(arguments)
arr.sort(function(a,b){
return a - b
})
arr.shift()
arr.pop()
return (eval(arr.join("+")) / arr.length).toFixed(2)
}
//不转换数组的做法
function avgFn(){
[].sort.call(arguments,function(a,b){
return a - b
});
[].shift.call(arguments);
[].pop.call(arguments);
return (eval([].join.call(arguments,"+")) / arguments.length).toFixed(2)
}
console.log(avgFn(9.8,9.7,10,3.0,9.9,9.6,9.5))
try\catch
//下面代码执行会报错,如果用try\catch捕获了异常信息,不影响下面的代码继续执行; 如果try中的代码执行出错了,会默认的去执行catch中的代码
// try {
// console.log(num)
// } catch (e) {//形参必须要写
// console.log(e.message) //num is not defined 可以收集当前代码的报错的原因
// }
// console.log('ok')
//有时候即想捕获到错误的信息,又想不让下面的代码继续执行
try {
console.log(num)
} catch (e) {//形参必须要写
console.log(e.message) //num is not defined 可以收集当前代码的报错的原因
// -> 手动抛出一条错误信息,终止代码执行
throw new Error('错误')
// new ReferenceError -> 引用错误
// new TypeError ->类型错误
// new RangeError -> 范围错误
}
console.log('ok')
sort用法
//sort
let ary = [12,3,14,15,23,1,13,15,35,16,51,10,9,4,18]
ary.sort(function(a,b){
// a -> 每一次执行匿名函数的时候,找到的数组中的当前项
// b -> 当前项的后一项
return a - b
// return a - b; 升序 如果 a > b , 返回的 > 0, a 和 b 交换位置
// return b - a; 降序 如果 b > a , 返回的 > 0, a 和 b 交换位置
})
ary.sort(function(a,b){
return 1 //不管 a 和 b 谁大,每一次都返回一个大于0的数,也就是每一次a 和 b都要交换为知,最后的结果就是原有的数组倒过来排序了 ==> reverse
})
JSON
//JSON不是一个单独的数据类型,它只是一种特殊的数据格式 -> 它是对象数据类型的
// var jsonObj = {
// "name":"小红",
// "age" : 22
// }
//在window浏览器中,提供了一个叫做JSON的数学,它里面提供了两个方法:
// -> window.JSON
// 1、JSON.parse -> 把JSON格式的字符串转换为JSON格式的对象
// 2、JSON.stringofy ->把JSON格式的对象转换为JSON格式的字符串
let jsonObj = {
name:"小红",
age: 22
}
let jsonStr = JSON.stringify(jsonObj)
console.log(jsonStr) //{"name":"小红","age":22}