1.变量
1.1var,let,const
var 全局作用域和函数作用域,及只要申明的代码执行了,下面的代码也能使用该变量如例一
挂载带wondow上,可以先打印后声明(打印undefined,不会报错,这就是变量提升),可重复申明,后申明的会覆盖前面申明的
let 块级作用域,声明的变量只能在当前块{}的申明后面与子块{{}}中使用,for()里申明的变量只能作用于for{}中
不会挂载带wondow上,不能先打印后声明(会报错,打印与申明之间会形成暂时性死区,let变量也会提升,但是暂时性死区的缘故导致报错)
子块可调用父块变量,如果子块也申明跟父块一样的变量名,则子块的变量同意遵循在当前块{}的申明后面与子块{{}}中使用
//例一
if (1) {
var a = 11;
let b= 22;
}
console.log(a); //正常,打印11,如果if(false)就不正常
console.log(b); //不正常
//例二
var a = [];
for (let i = 0; i < 5; i++) {
a[i] = function () {
console.log(i);
};
}
//for (let i = 0; i < 5; i++) 打印3,每循环一次,i的作用域仅限该次循环{}中
//for (var i = 0; i < 5; i++) 打印5,循环体已结束,此时调用打印最终结果5
a[3]();
//例三
var a = 11;
console.log(window.a); //打印11
let a = 11;
console.log(window.a); //打印undefined
//例四
console.log(a); //打印undefined
var a = 11;
console.log(b); //报错
var b = 11;
//例五
let a = 11;
{
console.log(a); //正常,使用父级a
}
let b = 11;
{
console.log(b); //不正常,下面有b的申明,所以b是当前块的b而不是父级b
let b = 11;
}
let c = 11;
{
let c = 22;
console.log(c); //正常 打印22
}
const 常量,不能重新赋值(变量重新等于其他的值就报错),对象和数组可以修改里面的值,其他规则与let一样
const a = XXX;
a = YYY; //重新赋值报错
a.bb = ZZZ; //正常
a.push = XXx; //正常
1.2解构赋值
基本用法:数组的解构用[],对象的解构用{},等号左边是解构出来的变量,右边是数据源,可以全部解构,也可解构部分,可赋默认值,也可用剩余扩展符...rest
例一
let [a,b] = [1,2,3];
console.log(a); //1
console.log(b); //2
例二
let {age,name,score} = {name:11,age:22} //对象必须用大括号,[age,name]报错,里面的键跟后面的键保持一致,不一致的取得是空
console.log(age); //22
console.log(name); //11
console.log(score); //undefined
数组的...运算符,数组解构可在数组最后面使用...运算符,表示数据源剩余数据,打印的是个数组
例一
let [a,b,...c] = [1,2,3,4,5];
console.log(a); //1
console.log(b); //2
console.log(c); //[3,4,5]
对象的k:v解构模式,对对象的键取别名解构,此时原键名被新的别名取代,使用必须用别名
例一
let {yingyu:english} = {yingyu:88};
console.log(english); //打印88
console.log(yingyu); //报错
解构赋值也可用作函数参数
例一
function fun([a,b]){
console.log(a+b);
}
fun([1,2]); //打印3
例二
function func({math,english}){
console.log(math+english);
}
func({math:60,english:70}); //打印130
1.3其他
2,数组
2.1form 数组对象转数组,静态方法,对象的属性名必须与数组的下标对应,必须有length属性,length是几,生成的数组就有几项,生成数组的下标对应对象的属性名,对不上就是undefined
例如
let obj={
0:11,
1:22,
length:2
}
let arr = Array.from(obj); //[11,22]
console.log(arr);
2.2of,静态方法,将一组值转成数组 let arr = Array.of(1,2,3,4);
2.3fill 对象方法,使用给定值,填充一个数组
let arr7=new Array(5).fill(3);
console.log(arr7);//[ 3, 3, 3, 3, 3 ]
2.4find,对象函数,遍历数组,返回第一个返回true的项,找不到返回undefined,参数是回调函数,回调函数有三个参数,代表数组的值,下标,原数组
let arr = [1,2,3,4,3];
let theItem = arr.find(function(val,index,a){
if (val%3 === 0) {
return true;
}
});
console.log(theItem); //3
2.5findIndex,对象函数,遍历数组,返回第一个返回true的下标,找不到返回-1,用法同find
let arr = [1,2,3,4,3];
let theItem = arr.findIndex遍历数组(function(val,index,a){
if (val%3 === 0) {
return true;
}
});
console.log(theItem); //2,3的下标正式2
2.6includes(str)是否包含某些,返回布尔型
2.7for of遍历
let arr = [11,22,33];
for(let v of arr){}
for(let(k,v) of arr.entries){}
3,对象
3.1属性简洁表示,如果kv相同可省略v, let name = 'Li'; let obj = {name};//相当与{name:name}
3.2属性名表达式,属性名用[]包裹,及表达式变量名,即[变量名] 变量值为属性名 let key = 'name'; let obj = {[key]:'Li'}; console.log(obj.name);
3.3新增方法
is assign keys values entries freeze
4,类
4.1es5写法
function Person(name,age){
this.name = name;
this.age = age;
this.sayAge = function(){
return this.age;
}
}
Person.prototype.sayName = function(){
return this.name;
}
function Ming(){}
Ming.prototype = Person.prototype; //继承Person类的原型方法
let p = new Person("Li",12);
console.log(p.sayName());
4.2 es6写法
class Anim{
constructor(name,age){
this.name = name;
this.age = age;
}
function sayName(){
console.log(this.name);
}
function sayAge(){
console.log(this.age);
}
}
//继承
class Dog extend Anim{
constructor(name,age,color){
super(name,age);
this.color = color;
}
function sayColor(){
console.log(this.color);
}
//重写父级方法
function sayName(){
console.log('dog name'+ this.name);
}
}
let dog = new Dog('Li',12,'red');
dog.sayName();
5,函数
5.1默认值
function(a=1,b=2){} //普通写法
function([a=1,b=2]=[]){} //数组解构写法
function({a=1,b=2}={}){} //对象解构写法
5.2rest参数(形式为...变量名),用于获取函数的多余参数,必须放在函数形参最后面
function demo4(a,b,...abc){console.log(abc);}
demo4(1,2,3,4,5);//[ 3, 4, 5 ]
5.3严格模式,只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。一般把严格模式加在全局
5.4name属性,函数的name属性,返回该函数的函数名。
function demo(){}
demo.name //demo
5.5箭头函数 ()=>{}
(1)this作用域
(2)不可做构造函数
(3)没有arguments对象,使用rest参数替代
5.6call,apply,bind,都是为了改变this指向,第一个参数是this执行,不用改变执行,就传null
call(this, 1, 2,...); //第一个参数是函数里this的指向,后面的参数与aa函数的参数对应(不对应有几个取几个),会直接执行函数
apply(this,[1,2,...]) //第一个参数是函数里this的指向,第二个参数是个数组,跟函数的参数对应,会直接执行函数
bind(this,1,2,...) //第一个参数是函数里this的指向,后面的参数与aa函数的参数对应(不对应有几个取几个),不执行函数,要执行,后面加()
例如取最大值
Math.max(1,2,3)
let arr = [1,2,3];
console.log(Math.max(...arr ));
console.log(Math.max.apply(null,arr))
console.log(Math.max.call(null,...arr))
console.log(Math.max.bind(null,...arr)()) //不会执行,要执行,后面加()
6,symbol类型
6.1原始数据类型(其他六种数据类型string,number,boolean,null,undefined,array,object),表示独一无二的值
6.2使用方式如下,直接调用Symbol方法,可带一个字符串参数
let s = Symbol();
console.log(typeof s); //symbol
console.log(s); //Symbol()
let ss = Symbol('haha');
console.log(typeof ss); //symbol
console.log(ss); //Symbol(haha)
6.3在对象中作为对象的属性使用,操作属性必须用[变量名]
let s = Symbol();
let obj = {
[s] : 100
}
console.log(obj[s]); //100
6.4对象中Symbol属性,无法通过for in,Object.keys() 遍历出来
要遍历出来,可以用Object.getOwnPropertySymbol(obj) 或者 Reflect.ownKeys(obj)遍历出来
7,模块module
7.1 import导入 export导出基本使用
import script标签必须添加type='module'属性,import from '文件路径',模块文件不用通过src引入
import * as f from xxx 导入所有项目,要获取export default,可用f.default
export可导出变量,常量,函数,对象,类,可单独导出,也可通过export {name,sayName,Person}进行多个导出,注意一点,函数的作为多个导出必须放大括号内
export普通导出的项,import 必须放到大括号内导入
export default默认导出项,模块文件内最多只有一个,import不用放大括号内导入
./js/index.js代码如下
export const name = 'Li'
export function sayName(){
}
export const man={
}
export default class Person{
function constructor(){
}
}
<script type='module'>
import Person,{name,sayName,man} from './js/index.js'
</script>
8,代理
8.1proxy
//通过代理,操作源数据,源数据可以是对象,也可以是数组
//target 源数据,key 要操作的数据key,value 要对key赋的值,receiver代理对象
let obj={
name:'lilei',
age:21,
sex:'男'
}
obj=new Proxy(obj,{
get(target,key,receiver){
//return target[key] //为什么不用这种做法,这种做法也能达到目的,但是target[key] 本身也是获取数据,导致也走了某个代理,所以最好用下面这种写法
return Reflect.get(target,key,receiver);
},
set(target,propKey,value,receiver){
//return target[key] = value
return Reflect.set(target,propKey,value,receiver);
}
});
console.log(obj.name);//1. false---- 2.结果:lilei
obj.name='小明';
console.log(obj.name);//结果:小明
8.2defineProperty
let obj={
name:'Li',
age:21,
sex:'男'
}
let internalName = obj.name; //必须需要这个中间变量
Object.defineProperty(obj,'name',{
get:function(){
//千万不能写出obj.name 这么写会导致回调陷阱,即获取操作又走到get,又执行obj.name,获取操作又走到get,最终导致内存不足的错误Maximum call stack size exceeded
return '老名字是:'+internalName
},
set:function(newValue){
internalName = '新名字是:'+newValue
}
});
console.log(obj.name);
obj.name = 11
console.log(obj.name);
9,扩展运算符
10,迭代器与生产器
10.1迭代器Iterator,一种新的遍历机制,通过next()获取遍历得到的值,值的形式为{value:xxx,done:false};当值的done为true时,遍历不出东西了
举例:将数组使用迭代器遍历
let arr = [11,22];
let i = arr[Symbol.iterator]();
console.log(i.next());//{value: 11, done: false}
console.log(i.next());//{value: 22, done: false}
console.log(i.next());//{value: undefined, done: true}
10.2生成器Generator,一个函数,通过yield表达式,中断执行,应用为使异步代码同步执行
10.2.1生成器基本声明方式
function* func(){
yield 1;
yield 2;
return 3;
}
let g = func(); //返回一个生成器对象,但是函数里面的代码不会执行
console.log(g.next()); //{value: 1, done: false} 调用next方法才会执行函数里面的代码,遇到yield就终止,next方法的返回值为yield的值
console.log(g.next()); //{value: 2, done: false}
console.log(g.next()); //{value: 3, done: true} value为return的值,如果生成器函数没有return,value的值就为undefined,done为true表示遍历完成
10.2.2next带参数的形式
yield会中断代码的执行,所以let x = yield 1;不会执行,需要在下次next执行,而x就等于下次next的参数
function* add(){
let x = yield 1;
let y = yield 2;
return x+y;
}
let g = add();
console.log(g.next()); //yield 1; 打印{value:1,done:false}
console.log(g.next(11)); //let x = 11; yield 2; 打印{value:2,done:false}
console.log(g.next(22)); //let y = 22; return x+y; 打印{value:33,done:true}
10.2.3应用实例
例一解决回调地狱
let request = (url)=>{
$.get({
url:url,
success(res){
g.next(res); //取得数据,把数据付给main函数里的res变量,g是下面生成器变量,已经变量提升了,let也会提升,这种异步先使用在声明不会产生错误
}
});
}
function* main(){
let res = yield request('https://v1.yiketianqi.com/api?unescape=1&version=v63&appid=&appsecret=');
console.log(res);
console.log('请求完成,继续操作');
}
let g = main();
g.next(); //先让main跑起来,跑到yield里面发送请求
最终打印结果
/*
{errcode: 100, errmsg: '缺少注册参数appid或appsecret 请仔细看文档,账号注册地址 http://tianqiapi.com'}
请求完成,继续操作
*/
例二 按照loading 请求数据 loadClose的顺序完成
function loading(){
console.log('loading');
}
function requestData(){
setTimeout(()=>{
let data = '这是模拟的数据'
console.log('获取了初始数据');
g.next(data);
},1000);
}
function loadClose(){
console.log('loading close');
}
function* load(){
loading();
let res = yield requestData();
console.dir('获取异步数据:'+res);
loadClose();
console.log('loading完成,继续操作');
}
let g = load();
g.next();
打印结果
/*
loading
获取了初始数据
获取异步数据:这是模拟的数据
loading close
loading完成,继续操作
*/
11,promise async await
12,set与hashmap
13,字符串
13.1模板字符串,``符号包裹的则是模板字符串,里面${变量名}是变量
例如
let name = 'Li';
let str = `${name}爱洗澡`
console.log(str); //Li爱洗澡
13.2标签模板
13.3新增函数
13.3.1 includes 是否包含字符串
13.3.2 startsWith 是否包含字符串
13.3.3 endsWith 是否包含字符串
13.3.4 repeat 将字符串重复几次,不会改变源字符串数据
例子
let str = '我爱北京'
console.log(str.includes('爱'));//true
console.log(str.startsWith('我'));//true
console.log(str.endsWith('我'));//false
console.log(str.endsWith('北京'));//true
console.log(str.repeat(2)); //我爱北京我爱北京
console.log(str); //我爱北京
14,原型和原型链
14.1原型是保存公共内容(公共变量,公共函数==)的区域。申明一个类,就会自动创建一个原型prototype
14.2类实例化后的对象,会为对象添加一个__proto__属性,该__proto__属性指向类的prototype,所以对象.__proto__ === 类的prototype
function Anim(){
}
let a = new Anim();
console.log(Anim.prototype === a.__proto__); //true
//a.__proto__指向类的原型,打印的是cunstructor Anim() f [[prototype]]
//a.__proto__.__proto__指向Object的原型,打印的是cunstructor Object() f [[prototype]] 还有大量方法hasOwnProperty,isPrototypeOf,propertyIsEnumerable==
//object的__proto__是显示的,在控制台能看到,上面的控制台看不到,但能打印出来
//a.__proto__.__proto__.__proto__ //打印null,最终的原型是null
console.log(a.__proto__.__proto__.__proto__);//打印null
14.3类的方法申明在原型上的作用
(1)节约内存
function Anim(){
}
Anim.prototype.sayName = function(){}
let a1 = new Anim(); //不会为对象开辟sayName方法的内存,从原型上取
let a2 = new Anim(); //不会为对象开辟sayName方法的内存,从原型上取,这样sayName只要在原型上申明一次即可,无论创建多少对象,都可以从原型获取这个方法
(2)通过原型继承
function Anim(){}
Anim.prototype.name = 'aa'
function Dog(){}
Dog.prototype = Anim.prototype
let d = new Dog();
console.log(d.name);
14.4数组的对象原型是Array
14.5对象的原型是Object
let arr = [];
//打印的是cunstructor Array() f [[prototype]] 还有大量跟数组有关的方法find,findindex,includes,indexof==
console.log(arr.__proto__);
let obj = {};
console.log(obj.__proto__);
14.6类的原型prototype,对象和数组的对象原型是__proto__.类实例化对象的__proto__指向类的prototype,__proto__本身也是个对象,它的__proto__指向下级(可能是object),最终指向null
15,深拷贝与浅拷贝
16,防抖和节流
16.1防抖debounce:是指单位时间内,频繁触发事件,只执行最后一次(每次触发事件,取消上次的执行,重新计算触发时间,导致最后一次执行的时间延后)
16.1.1 场景
(1)搜索框搜索输入,只需用户最后一个字符输入完,在发起请求
(2)手机号,邮箱验证检测
(3)div滑动事件,鼠标不动了,会在1s后获取坐标
例如
let box = document.querySelector('.box');
function mousemove(e){
box.innerHTML = e.clientX+" "+e.clientY;
}
function debounce(fn,t){
let timer;
return function(e){
if (timer) clearTimeout(timer);
timer = setTimeout(fn.bind(null,e),t);
}
}
box.addEventListener('mousemove',debounce(mousemove,500));
16.2节流throttle:是指一定时间内执行的操作只执行一次,即每段时间内只执行一次,resize,scroll
例如
let box = document.querySelector('.box');
function mousemove(e){
box.innerHTML = e.clientX+" "+e.clientY;
}
function throttle(fn,t){
let timer = null;
return function(e){
if (!timer){
timer = setTimeout(function(){
fn(e);
timer = null; //为什么不用clearTimeout,clearTimeout放在setTimeout里面无法清除定时器
},t);
}
}
}
box.addEventListener('mousemove',throttle(mousemove,500));
16.3 效果总结,
防抖,鼠标一致动,坐标一致不变,直到鼠标停止500毫秒后,坐标才变成鼠标最后的位置坐标
节流,鼠标一致动,坐标每隔500毫秒变一次
17,addEventListener的几点研究
///防抖和节流的知识储备
let box = document.querySelector('.box');
function mousemove(e,a=1,b=2){ //e必须放最前面,因为是直接调用,不带其他参数
box.innerHTML = e.clientX+" "+a;
}
function mousemoveRest(a,b,e){ //为函数绑定其他数据,e必须放最后面
box.innerHTML = e.clientX+" "+a;
}
function mousemoveGo(){
return function(e){
box.innerHTML = e.clientX;
}
}
//addEventListener第二个参数是个函数
//绑定事件,回调函数不会执行
box.addEventListener('click',mousemove);
box.addEventListener('click',mousemoveRest.bind(null,1,2)); //绑定其他参数
//绑定事件,回调函数会执行,下面这种情况导致触发click,也不会执行mousemove,mousemove里也没有e
//因为addEventListener第二个参数是个函数,而执行过后的mousemove不是一个函数,导致出问题
box.addEventListener('click',mousemove()); //函数名称后只要带上(),就会立即执行
//绑定事件,回调函数会执行
//相当于绑定里mouseMoveGo里面的那个匿名函数
//所以触发click,只会执行匿名函数里面的代码,匿名函数外的代码不会再次执行,e也再匿名函数内
box.addEventListener('click',mouseMoveGo());
//相当于下面这种写法
let go = mousemoveGo(); //go是mousemoveGo返回的那个匿名函数
box.addEventListener('click',go);