json
json字符串
== JSON是一种特殊的字符串格式,本质是一个字符串
== 但是长得像对象和数组,里面的key和value如果是字符串格式,都用双引号包裹(必须是双引号)
json方法
JSON.parse(json字符串):可以把json字符串转换成对象或者数组
var obj = JSON.parse(jsonObj);
var arr = JSON.parse(jsonArr)
console.log(obj);// obj就是我们的js对象
console.log(arr);// arr就是我们的js数组
JSON.stringify(对象或者数组):可以把对象或者数组转换成json字符串
var obj2 = {
a:1,b:2,gender:'男'
}
var arr2 = [
{a:1,b:2,gender:'男'},
{a:10,b:20,gender:'女'}
]
var jsonObj2 = JSON.stringify(obj2);
var jsonArr2 = JSON.stringify(arr2)
console.log(jsonObj2);// json字符串
console.log(jsonArr2);// json字符串
call/apply/bind改变this指向
- call方法
call方法是附加在函数调用后面使用,可以忽略函数本身的this指向
语法:函数名.call(‘要改变的this指向’,‘函数实参1’,‘函数实参2’,…)
-
apply方法
apply方法是附加在函数调用后面使用,可以忽略函数本身的this指向
语法:函数名.apply(‘要改变的this指向’,[‘函数实参1’,‘函数实参2’,…])
3.bind方法
function fn(a,b){
console.log(this);
console.log(a)
console.log(b)
}
fn(10,20);// 本次fn()直接调用,this是window
console.log("=======")
var obj = {name:'Jack'}
var newFn = fn.bind(obj);// bind调用的时候,不会执行fn函数,而是返回一个新的函数,这个新的函数是一个改变了this指向以后的函数
newFn(1,2);// 调用newFn(1,2) 就是调用一个长的和fn一模一样的函数,只不过里面的this指向改成了obj
区别:
+ call和apply可以改变函数里面的this指向,会立即执行函数
+ bind可以改变函数里面的this指向,返回一个新的改变了this指向的函数,不回立即执行函数
+ call和apply区别:传递实参的方式不同
let const声明
相同:
1 let和const不允许重复声明变量
2.let和const声明的变量不会在预解析的时候解析(也就是没有变量声明提示)
3.let和const声明的变量会被所有代码块限制作用域
区别:
1.let声明的变量的值可以改变,const声明的变量的值不可以改变
2.let声明的时候可以不赋值,const声明的时候必须赋值
箭头函数
-
特性:
1.匿名函数(赋值函数)才能简写成箭头函数 2.箭头函数内部没有this,箭头函数的this是上下文的this(就是他所处的环境里面的this,也就是他不会改变this) 3.箭头函数内部没有arguments这个参数集合 4.函数的行参只有一个的时候,可以不写(),其余情况必须写 5.函数体只有一行代码的时候,可以不写{},还会自动return
函数默认值:
在es6我们可以直接把默认值写在函数的行参位置
function fn(a=10){
console.log(a)
}
fn(); // 不传递参数的时候,函数内部的a就是10
fn(20);// 传递参数20的时候,函数内部的a就是20
解构赋值(就是快速的从对象或者数组中取出成员的一个语法方式)
-
解构对象
const obj = { name:'Jack', age:18, gender:'男' } // 前面的{}表示我要从obj这个对象中获取成员 // name,age,gender都得是obj中有的成员名 // obj必须是一个对象 let {name,age,gender} = obj; console.log(name) console.log(age) console.log(gender)
-
解构数组
const arr = ['jack','rose','tom']; // 前面的[]表示从arr这个数组中获取成员 // a,b,c分别对应这个数组的索引0,1,2 // arr必须是一个数组 let [a,b,c] = arr; console.log(a) console.log(b) console.log(c)
模板字符串
-
``表示字符串
-
${}表示变量
展开运算符(…)
// let arr = [1,22,333,4444,55555]
// ...arr:就是把数组的壳拆掉,把里面的内容展开出来
for in和for of
-
for in(遍历索引或成员名)
for(let key in obj){ console.log(key);// 成员名 } for(let index in arr){ console.log(index);// 索引 }
-
for of(遍历值,只能遍历数组)
for(let val of arr){ console.log(val);// 数组中的元素 }
新增数据类型
- 简单数据类型
-
BigInt:可以表示任意大的整数
-
Symbol: 每个symbol类型的值都是唯一的
- 复杂数据类型
- Set:是值的集合,类似数组,但是这个集合里面的元素不能重复
- Map:类似对象,最不过,对象的成员名只能是字符串类型,Map他的成员名可以是任意类型
对象书写
成员名和成员值的变量名相同可以简写(原来的 成员名:变量名 直接写成 变量名)
let age = 18;
let gender = "男";
let name = 'rose'
// let obj = {
// name:name,
// age:age,
// gender:gender,
// fn:function (){
// console.log('fn')
// },
// }
// es6语法中,上面的对象
// 成员名和成员值的变量名相同可以简写
// 原来的 成员名:变量名 直接写成 变量名
let obj = {
name,
age,
gender,
fn(){
console.log('fn')
}
}
同步和异步
-
同步任务
同步任务指 主线程 上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务。
理解:让代码按照从上往下正常流程执行的,就是同步
-
异步任务
异步任务指 不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程
理解:改变程序正常执行顺序的操作,就是异步操作。异步有 **
setTimeot
和setInterval
**函数,ajax操作等
es6中类的概念
深拷贝第一层
-
assign方法
var target = Object.assign({}, obj1, obj2); //把obj1和obj2复制到前面的空对象,再把结果给target
-
…展开运算符
var obj2 = { ...obj }; //展开obj再组成对象,结果给obj2
删除对象本身的属性和方法(不能删除原型上)
- delete p.name;
了解函数
+ 函数的定义阶段
1 开辟一个存储空间
2 把函数代码当成字符串存储在这个空间里面
== 函数里面的变量只是一个字符串,不会解析
3 把存储空间的地址赋值给函数名
+ 函数的调用阶段
1 通过函数名找到函数的存储空间
2 开辟一个函数的执行空间
3 把函数存储空间里面的代码复制一份到这个执行空间
4 执行形参赋值
5 执行预解析
6 执行函数里面的代码
7 函数执行完成以后,这个执行空间就销毁了
函数调用
1 每次调用函数都开辟一个函数执行空间
2 在这个空间里面进行形参赋值
3 在这个空间里面进行预解析
4 执行代码
5 代码执行完毕执行空间销毁
一个不会被销毁的函数执行空间–闭包
-
形成闭包要具备三个条件
== 函数A内部返回一个函数B
== 这个函数B可以操作函数A的私有变量
== 返回的函数B在函数A外部有一个变量接收 -
就形成了一个不会被销毁的函数执行空间
+ 我们把函数A这个不会被销毁的执行空间叫做函数A的闭包空间
+ 把函数B叫做函数A的闭包函数
+ 官方: 闭包 --> 函数里面的函数 -
闭包特点
1 延长了变量的生命周期 == 优点:延长了变量的生命周期,后期还可以继续使用 == 缺点:延长了变量的生命周期,导致内存空间无法释放 2 可以使用函数内部的私有变量 == 优点:只能通过闭包函数来使用函数内部的私有变量 == 缺点:闭包空间一直内有销毁,一直被占用 3 保护了函数的私有变量(所有函数都有这个特点) == 优点:函数内部的变量在外部无法访问 == 缺点:延长了变量的生命周期,导致内存空间无法释放 闭包虽然有很多优点,但是有一个致命的缺点 == 长期占用内存,导致内存无法释放 == 容易导致内存泄漏 == 闭包:慎用 == 有别的办法就不要用闭包
防抖和节流
防抖:一个函数连续多次触发,我们只执行最后一次。
// fn是要节流的函数,wait是时间间隔,默认500毫秒
const myDebounce = (fn, wait = 500) => {
// 缓存一个定时器
let timer = null
// 使用闭包(这样节流函数复用时,不会相互影响,且不污染全局变量)
return function( ...args ) { // ES6剩余参数收集参数
// 如果在500毫秒内再次触发,即timer存在,此时清除掉这个timer
// 这里实现了执行只最后一次
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args) // 利用apply绑定this,同时展开args数组并传参
}, wait)
}
}
节流:一个函数连续多次触发,我们按照一定的时间间隔多次执行。
// 缓存一个定时器
let timer
// 使用闭包(这样节流函数复用时,不会相互影响)
return function( ...args ) {
// 如果在500毫秒内再次触发,即timer存在,此时return,等待这个timer执行完毕。
// 这里实现了时间间隔
if (timer) return
// 这里表示第一次触发,或是上一个timer执行完毕。就可以重新开启一个定时器。
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, wait)
}
}
继承(两个构造函数之间的关系)
-
new的时候干了什么
1 开辟一个内存空间,this指向这个空间 2.this的变量赋值 2 执行构造函数里面的代码 3 自动把this返回 4 把this存储的地址赋值给某个变量
-
原型继承
== 如果希望构造函数B能使用构造函数A里面定义的方法和属性 == 可以这样写: 构造函数B.prototype = 构造函数A的实例 原型继承的缺点: 1 继承来的属性不再本身上,而是在原型上 2 参数在两个地方传递,不方便 原型继承的优点:继承了父类的所有属性和原型链上的内容
-
例子
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHi = function () { console.log("hello world"); }; var p = new Person("rose", 12);
-
-
构造函数继承
借用构造函数继承,可以把父类上的属性继承到子类的实例本身上 但是父类的原型上的内容都没有继承到
-
例子
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHi = function () { console.log("hello world"); }; function Student(name, age, banji) { // 就是在这里把Person构造函数里面的代码执行一遍 Person.call(this, name, age); this.banji = banji; }
-
-
组合继承
== 不管是原型继承还是借用构造函数继承 == 都有优缺点 == 所以我们结合他们的优点有了组合继承 == 组合继承 = 原型继承 + 借用构造函数继承
-
例子
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHi = function () { console.log("hello world"); }; function Student(name, age, banji) { // 1 执行Person这个父类的构造函数 Person.call(this, name, age); this.banji = banji; } // 2 原型继承下来 var p = new Person(); delete p.name; delete p.age; Student.prototype = p; Student.prototype.sayName = function () { console.log("sz2202"); }; // 实例化Student var s = new Student("rose", 18, "sz1908"); console.log(s);
-
-
es6类的继承
class 子类 extends 父类{ constructor(){ super() } }
instanceof
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上,即可以判断一个对象是否是一个构造函数的实例
Object.defineProperty()
-
以前更新属性和方法:
var obj = { name: "rose" }; // 给对象添加属性 obj.age = 12; // 修改对象的属性值 obj.name = "jack"; console.log(obj);
-
现在:Object.defineProperty()
== 更具有可控性 == 比如:属性值是否可修改能设置 == 比如:属性值可以设置是否能通过for...in遍历 == 比如:你修改了属性值,会通知我 == 比如:你获取了属性值,会通知我 == ....
-
Object.defineProperty()使用:
- Object.defineProperty(要定义属性的对象,要定义的属性名,对属性的描述)
== 对属性的描述是一个对象,描述分为两种情况 == 数据描述符: 就是描述属性值,值是多少,是否可 == 值:value == 是否可以for...in:enumerable == 是否可以修改:writable == 存取描述符: == set:function(val){} == 函数在设置属性值的时候触发 == 形参val就是你设置的属性值 == get:function(){} == 函数在获取属性值的时候触发 == 函数的返回值就是属性值
数据双向绑定
-
双向数据绑定 == 更新数据,界面自动更新 == 界面更新,数据也会更新 举例 1 封装一个标签属性cyr-value,只要你设置: cyr-value='变量名' == 表单的value值和变量名进行双向绑定 2 封装一个标签属性cyr-html,只要你设置cyr-html='变量名' == 变量值变化,标签的内容也会变化
设计模式
-
概念:就是针对特殊情况,特殊环境,特殊时期,某种问题的优化的解决方案
-
同一个问题,在不同的事情,不同的情况,不同的环境下,可能要使用不同设计实际模式
-
设计模式三大类
== 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式 == 结构型模式,共七种:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 == 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
-
组合模式
- 适用场景:
- 1.存在一批组织成某种层次体系的对象
- 2.希望这一批对象或其中一部分对象实施一个操作
- 适用场景:
-
单例模式
-
规定一个类只有一个实例
-
例子
一次实现同一个实例对象的一个弹框 var TanKuang = (function () { // 真正的构造函数TanKuang隐藏在闭包空间里面 // 换个名字abc function abc() { this.text = "hello world"; } var instance = null; return function single() { if (instance === null) { instance = new abc(); } return instance; }; })(); // TanKuang就是自执行函数的返回值 // 就是函数single var t1 = new TanKuang(); var t2 = new TanKuang(); console.log(t1 == t2);//值一样就是两次创建的实例都是同一个
- 观察者模式
- 它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
-