JavaScript进阶

1.全局作用域:
js中所有声明的变量都存在window对象中。var声明的变量会提升,function的两种声明,第一种function test(){}, 函数会提前创建,所以在任何地方地方调用都不会报错;第二种函数声明var test = function(){};函数不会提前创建,如果在函数声明之前调用函数会报错

2.函数作用域:
调用函数时创建函数作用域,函数执行完毕后,销毁函数作用域;每调用一次函数,就会创建一个函数作用域,它们是互相独立的

3.使用工厂方法创建对象:

function createObj (name,age,gender){
const obj = new Object()
obj.name = name
obj.age = age
obj,gender = gender
return obj
}
var obj1 = createObj('孙悟空',18,'男')
var obj2 = createObj('猪八戒',19,'女')

4.构造函数:
使用工厂化创建的对象都是object类型,就导致无法区分出类型,所以一般都是使用构造函数。构造函数使用new关键字来调用

构造函数执行流程:
①.立刻创建一个新的对象
②将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
③逐行执行函数中的代码
④将新建的对象作为返回值返回

function Person(name,age,gender){
this.name = name
this.age = age
this.gender = gender
this.sayName = function(){
alert(this.name)
}
}
var per = new Person('孙悟空',19,'男')
var per2 = new Person('猪八戒',20,'男')
// per和per2叫做Person类的实例对象
//可以使用instanceof检查一个对象是否是一个类的实例,返回布尔值
//console.log(per instanceof Person)

5.原型对象:

1.我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype原型对象。
2.如果函数作为普通函数调用,那么这个prototype没有任何作用
3.如果函数作为构造函数调用,那么它所创建的每一个实例对象都有一个隐含的属性(proto)指向这个构造函数的prototype原型对象
4.原型对象就相当于一个公共的区域,所有同一个类的实例对象都可以访问到这个原型对象,所以我们可以将公共的东西放到这个原型对象中,这样不用分别为每个对象添加,优化了性能,也不会影响到全局作用域,就可以使每个对象都有该属性和方法
5.当我们访问对象的属性时,先从对象自身去寻找属性,当没找到时就去原型对象中寻找
6.使用in检查对象中是否有该属性,返回布尔值,如果原型对象中有该属性也会返回true:
console.log(‘name’ in person)
7.可以使用对象的hasOwnProperty()方法来检查对象自身中是否有该属性,不包括原型对象中的属性,返回布尔值

person.hasOwnProperty('name')
console.log(Person.prototype.__proto__.hasOwnProperty("hasOwnProperty"))
 // hasOwnProperty方法存在原型的原型当中

6、forEach遍历数组

forEach里面的是回调函数,遍历的数组有多少个值,回调函数就会执行多少次,回调函数有三个参数,第一个参数是数组中的元素,第二个参数是数组的索引,第三个参数是正在遍历的数组

const arr = ['ashin','孙悟空','猪八戒','12']
        let newArr =[]
        arr.forEach((value,index)=>{
           newArr.push({
            index:index
            name:value,
           })
        })
        console.log(newArr)

7.map():对数组中的元素调用函数进行处理,并且把处理结果放在一个新数组中返回

var Array = [1,2,3];
 var result =Array.map(function(item){
    return item* 2;
 });
 console.log(result);//[2,4,6]

8.filter():筛选数组中符合条件的所有元素,并且放在一个新数组中

var Array= [1,2,3,4,5,6,7];
 var result = Array.filter(function(value){
     return value>5;
 });
 console.log(result);//[6,7]
 

9.find():用来返回数组中符合条件的第一个元素

var Array = [1,2,3,4,5,6,7];
 var result = Array.find(function(value){
     return value > 5;   //条件
 });
 console.log(result);//6

10.slice(start,end):

用来截取数组中的指定元素,有两个参数,第一个是截取开始位置的索引,第二个是截取结束位置的索引,参数可以是负数,表示从后往前算,该方法不会改变原有数组,需要返回到新数组中
var result = arr.slice(1,3)

11.splice(start,number,newValue):

用于删除数组中的指定元素,splice会影响到原数组,将指定元素从原数组中删除。有两个参数,第一个参数是开始位置的索引,第二个是删除的个数,第三个及以后参数是替换的值

12.数组去重

var arr = [1,2,3,4,4,5,4,3,2,1]
for(var i =0;i<arr.length;i++){
    for(var j = i+1;j<arr.length;j++){
        if(arr[i]==arr[j]){
            arr.splice(j,1)
            j--
        }
    }
}

13.ES6中使用Set()去重:

var arr=['a','b',1,2,1,3];
var newarr=new Set(arr);
console.log(newarr)  //输出 {"a", "b", 1, 2, 3}
let arr1=[1,2,3]
    let arr2=[1,3,4]
    let arr3=new Set([...arr1,...arr2]) //注意这里是对家中括号
    console.log(arr3); //输出 {1, 2, 3, 4}

14.call()、apply():

两个方法都是函数对象的方法,需要通过函数对象来调用,当对函数调用call()或apply()都会调用函数执行
可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this。
两者的区别: call() 传递参数,依次传递;apply()传递参数要用数组传递

function fun(a,b){
	console.log(this.name)
	console.log('a:',a)
	console.log('b',b)
}
var obj = {
	name:"obj",
	sayName:function(){
	alert(this.name)
	}
}
fun.call(obj,1,2)  //obj, 'a':1 'b':2
fun.apply(obj,[1,2])  //obj, 'a':1 'b':2

15.this的情况:

1.以函数的形式调用时,this永远都是window
2.以方法的形式调用时,this是调用方法的对象,即谁调用this就指向谁
3.以构造函数调用时,this是新创建的那个对象
4.使用call()或者apply()调用时,this是指定的那个对象

16.arguments:

是一个类数组对象,它也可以通过索引操作数据,也可以获取长度,在调用函数的时候,我们所传递的实参都在arguments中保存
arguments.length可以用来获取实参的个数
即使不定义形参,也可以通过arguments使用实参;arguments[0]表示第一个实参,arguments[1]表示第二个实参
arguments里边有一个属性callee,这个属性对应一个函数对象,就是正在执行的这个函数对象

17.闭包:

正常来说,函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。而闭包就是能够读取其他函数内部变量的函数,所以说,闭包可以简单理解成“定义在一个函数内部的函数,然后return出来。在本质上,闭包是将函数内部和函数外部连接起来的桥梁
闭包的用途:一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在外部函数f1调用后被自动清除。

function f1(){
    var n=999;
    function f2(){
			n++
      alert(n);
    }
    return f2;
  }
var result=f1();
result(); // 999
result(); // 1000
// 证明了函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
//为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

筛选两个数之间的所有数字

var arr = [1,2,33,44,4,55,42,3,2,1]
function between(a,b){
    return function(v){
        return v>a&&v<b
    }
}
const res = arr.filter(between(5,100))
console.table('res:',res)

闭包this的问题

var name = "The Window";

  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };

  alert(object.getNameFunc()()); // The Window
var object = {
    name : "My Object",
    getNameFunc : function(){
      return ()=>{  //使用箭头函数就可以指向父级的this。或者用This = this
        return this.name;
      };
    }
  };
alert(object.getNameFunc()()); // My Object

闭包的注意点:

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

18.数据类型
1.原始类型(基本类型、值类型):Number、String、Boolean、undefined、Null、symbol、bigInt
2.引用类型(对象):Object----》Arrary、Date、Math、Set、Map等等
原始类型和引用类型的赋值是不一样的,原始类型赋的就是一个值,而引用类型赋的是引用是一个指向,例如:

let str1 = {name:'xiaoming'}
let str2 = str1
str1.name = 'xiaohong';
//此时str2也改变了

注意:原始类型和引用类型的传参也是不一样的,原始类型作为参数,函数里面的操作不会影响到实参的值,但是引用类型传参会影响到实参的值

Promise

1.出现的意义: 解决回调地狱(在回调函数中嵌套回调)

2.promise的基本使用: promise是一个构造函数,通过new关键字实例化对象
语法

new promise((resolve,reject)=>{})
//promise接收一个函数作为参数,
//在参数函数中接收两个参数 :resolve,reject

3.promise实例有两个属性:state:状态 result:结果

4.promise的三种状态
第一种状态:pending(准备,待解决,进行中)
第二种状态:fulfilled(已完成,成功)
第三种状态:rejected(已拒绝,失败)

5.promise状态的改变: 通过resolve()和reject()改变当前promise对象的状态

const p = new promise((resolve,reject)=>{
	resolve('参数');  //调用函数,使当前promise对象的状态改为fulfilled
	reject('参数');  //调用该函数,使当前promise对象的状态改为rejected
})

promise状态的改变是一次性的,当调用了resolve()改变为fulfilled之后再调用其他就没用了

6.promise的then方法:

then方法函数有两个参数,两个参数都是函数:1.当promise的状态是fullfilled时执行;2.当promise的状态是rejected时执行,then方法里面的参数函数的形参就分别对应于resolve的参数和rejected的参数

const p = new Promise((resolve,reject)=>{
            resolve('成功调用了');
            //reject('失败调用了')
        })
        p.then((value)=>{
            console.log('成功了',value) //value参数就是resolve的参数,相当于形参对应实参
        },
        (err)=>{
            console.log('失败了',err)  //err就是rejected的参数 相当于形参对应实参
        })

then方法会返回一个新的promise,状态是pending

注意:
1.promise的状态没有改变,是不会执行then方法的,即promise没有执行resolve()或者reject(),状态是pending状态,即以下代码是没有执行

2.在then方法中,通过return将返回的的promise实例改为fulfilled状态,才可以接下来继续调用then方法,return的值就是下一个then的成功参数;如果在then方法中出现代码错误,会将返回的promise实例改为rejected状态

new Promise((resolve,reject)=>{
            resolve('123')
        }).then((value)=>{
            console.log('成功',value)
            return 456
        },(reason)=>{
            console.log('失败')
        }).then((value2)=>{
            console.log('成功2',value2)
        },(reason)=>{
            consol.log('失败了2')
        })

3.catch方法:
catch方法在什么时候被执行?
1.当执行rejected()时候
2.当promise出现错误代码时
3.手动输出错误
then: 正常返回的时候,Promise状态时fulfilled;报错的时候,promise的状态是rejected
catch: 正常返回时候状态也是fulfilled,报错的时候才是rejected,而不是执行了catch就是状态为rejected

var pp = new Promise((resolve,reject)=>{
            //reject();
            //console.log(a)
            throw new Error('出错啦!')
        })
        pp.catch((reason)=>{
            console.log('失败了',reason)
        })
        console.log(pp)

promise常规写法:

new Promise((resolve,reject)=>{
			// resolve()
			// reject()
        }).then((value)=>{
            //成功时被执行
            console.log(value)
        }).catch((reason)=>{
            //失败时执行
            console.log(reason)
        })

用promise解决回调地狱:

 function getData(url,data={}){
         return new Promise((resolve,reject)=>{
                $.ajax({
                    method:'GET',
                    url:url,
                    data:data,
                    success:function(res){
                        //修改promise的状态为成功,修改promise的结果res
                        resolve(res)
                    },
                    error:function(res){
                        reject(res)
                    }
                })
            })
        }
        getData('data1.json').then((data)=>{
            const {name} = data;
            console.log(name)
            return getData('data2.json',name)
        }).then((data)=>{
            const {phone} = data;
            console.log(phone)
            return getData('data3.json',phone)
        }).then((data)=>{
            const {sex} = data
            console.log(sex)
        })

async、await与Promise

1.执行async函数,返回的都是promise对象

async function test1(){
	return 1  //自动封装乘promise对象
}
async function test2(){
	retuen Promise.resolve(1)
}

2.Promise.then 成功的情况对应 await;
await后面跟promise对象

async function test3(){
	const p3 = Promise.resolve(3)
	p3.then(data=>{
	console.log('data',data)
})
const data = await p3  // 与p3.then()是一样的,都是输出3
console.log('data',data)  
}

await 后面跟一个值

async function test4(){
	const data = await 4  //相当于执行了 await Promise.resolve(4) ;又因为await相当于Promise.then
	console.log(data)   //4
}

await后面跟异步函数

async function test5(){
	const data = await test1()  
	console.log(data)  //输出1
}

promise.catch对应try…catch

async function test6(){
	const p6 = Promise.reject(6)
	try{
	const data6 = await p6
	console.log(data6)
}catch(e){
	console.log('e',e)  //捕获到错误,输出6
	}
}

async(异步)函数执行的顺序问题

async function test1(){
	console.log('a')
	const result = await test2()  //await后面的代码,可以看成是放在异步里面的代码,相当于放在setTimeout里面
	console.log('result:',result)
	console.log('b')
} 
async function test2(){
	console.log('c')
}
console.log('d')
test1()
console.log(e)
//执行顺序应该为:d,a,c,e,result:underfined,b

微任务与宏任务

宏任务:setTimeout,setInterval,DOM事件,AJAX请求,DOM渲染
微任务:Promise、async/await
结论: 微任务比宏任务执行时机早; 微任务>DOM渲染>宏任务

console.log(1)
Promise.resolve().then(()=>{
	console.log(2)
})
setTimeout(()=>{
	console.log(3)
},0)
console.log(4)
// 执行结果为: 1,4,2,3

Event Loop

event loop: 是一个执行模型,在不同的地方有不同的实现。浏览器和NodeJS基于不同的技术实现了各自的Event Loop。
event Loop: 开始的时候,会从全局一行一行的执行,遇到函数调用,会压入到调用栈中,被压入的函数被称为帧,当函数返回后会从调用栈中弹出
消息队列: 是暂时存放异步任务的地方,等同步代码执行完毕以后,EventLoop会从消息队列中一次取出异步任务放到调用栈再次执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值