前端知识大全

发布于:2022-11-28 ⋅ 阅读:(528) ⋅ 点赞:(0)


前言

在业余时间中,无聊的我在网上看了一些面试题和技术博文突然发现其中还有一些很不错的知识点就想收藏起来, 但是越到后面发现东西太多太杂再回头看的时候不好梳理和记忆,于是乎我决定在看的同时把它们记录起来汇总成一个知识大全,以便于之后方便与查找和理解记忆为此巩固自己的基本功。


一、三剑客

1.HTML

待更新中…

2.CSS

待更新中…

3.JS

  • JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式。
  • JavaScript在1995年由Netscape公司的Brendan Eich,在网景导航者浏览器上首次设计实现而成。因为Netscape与Sun合作,Netscape管理层希望它外观看起来像Java,因此取名为JavaScript。但实际上它的语法风格与Self及Scheme较为接近。
  • JavaScript的标准是ECMAScript 。截至 2012 年,所有浏览器都完整的支持ECMAScript5.1,旧版本的浏览器至少支持ECMAScript 3 标准。2015年6月17日,ECMA国际组织发布了ECMAScript的第六版,该版本正式名称为 ECMAScript 2015,但通常被称为ECMAScript 6 或者ES2015。

3.1 防抖(debounce)

官话:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时。也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间,那么防抖的情况下只会执行一次。
白话:在input框中,当用户连续不停的输入一些值之后只在规定时间内触发最后的一次的回调

防抖: <input id="input" type="text">

	const inp = document.getElementById('input')
    inp.addEventListener('input', function (e) {
        result(e.target.value)
    })
    let result = debounce(function (value) {
        console.log('value', value);
    }, 1000)
    function debounce(fn, time) {
        let timer
        return function (value) {
            // 这里的value是如何通过fn拿到的呢?利用了闭包能够读取其它函数内部变量的特点
            clearTimeout(timer)
            timer = setTimeout(() => {
                fn(value)
            }, time);
        }
    }

3.2 节流(throttling)

官话:固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期。
白话:在按钮中,当用户连续不停的点击触发事件之后只会在规定时间内触发最行的回调

节流: <button id="button">手速再快也是1s一次</button>

	const btn = document.getElementById('button')
        btn.addEventListener('click', throttling(function(e) {
            console.log('value', e.target.innerText);
        }, 1000))
    function throttling(fn, time) {
         let timer
         return function() {
             if (!timer) {
                 timer = setTimeout(() => {
                     // 改变this指向把e对象传出去
                     fn.apply(this, arguments)
                     timer = null
                 }, time);
             }
         }
     }

3.3 深拷贝和浅拷贝

3.3.1 浅拷贝

拷贝基本数据类型时,不受任何影响,当拷贝引用类型时,源对象也会被修改且浅拷贝只拷贝已存在对象的对象属性的引用,其余非对象属性是占用新的内存空间,并非与原对象共享。

  1. Array.prototype.slice()方法

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的 浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

 const arr = ['小明', '小红', { name: '小紫', age: 20 }]
 let newArr = arr.slice()
 newArr[0] = '小绿'
 newArr[2].age = 99
 console.log('arr', arr); // ['小明', '小红', {name: '小紫', age: 99}]
 console.log('newArr', newArr); // ['小绿', '小红', {name: '小紫', age: 99}]
  1. Array.concat()方法

concat方法不会改变this或任何作为参数提供的数组,而是返回一个 浅拷贝,它包含与原始数组相结合的相同元素的副本

const arr = ['小明', '小红', { name: '小紫', age: 20 }]
let newArr = [].concat(arr) // 或者let newArr = [...[], ...arr]
newArr[0] = '小绿'
newArr[2].age = 99
console.log('arr', arr); // ['小明', '小红', {name: '小紫', age: 99}]
console.log('newArr', newArr); // ['小绿', '小红', {name: '小紫', age: 99}]
  1. Object.assign()方法和ES6的扩展运算符

Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。
用扩展运算符对数组或者对象进行拷贝时,只能扩展和深拷贝第一层的值,对于第二层极其以后的值,扩展运算符将不能对其进行打散扩展,也不能对其进行深拷贝,即拷贝后和拷贝前第二层中的对象或者数组仍然引用的是同一个地址,其中一方改变,另一方也跟着改变。

const obj = { name: '小明', age: { num: 10 } }
let newObj = {}
Object.assign(newObj, obj) // 或者let newObj = {...obj}
newObj.name = '小亮'
newObj.age.num = 100
console.log('obj', obj); // {name: '小明', age:{num: 100}}
console.log('newObj', newObj); // {name: '小亮', age: {num: 100}}

3.3.2 深拷贝

深拷贝就是把一个对象,从内存中完整的拷贝出来,从堆内存中开辟了新区域,用来存新对象,并且修改新对象不会影响原对象

  1. 用JSON.stringify和JSON.parse

可以深拷贝的数组和对象,但是不能拷贝函数,可以进行对象或者数组的嵌套拷贝

const arr = ['小明', '小红', { name: '小紫', age: 20 }]
let newArr = deepClone(arr)
newArr[0] = '小绿'
newArr[2].age = 99
console.log('arr', arr); // ['小明', '小红', {name: '小紫', age: 20}]
console.log('newArr', newArr); // ['小绿', '小红', {name: '小紫', age: 99}]
function deepClone(data) {
    let _obj = JSON.parse(JSON.stringify(data))
    return _obj
}
  1. 用递归

函数 caller 运行时,调用其他函数 called ,js会在调用栈中新开一个调用帧存储作用域和上下文信息,而caller的调用帧信息仍需要保存。而内存中调用栈存储信息有限,递归情况下,如果递归层次过深会导致调用栈耗光而引起stack overflow —— 爆栈。

const obj = {
  name: '香风智乃',
   age: 18,
   sex: '女',
   address: {
       id: '001',
       title: '咖啡馆',
   },
   color: ['蓝色', '白色'],
   say() {
       console.log('唱歌');
   }
}

let newObj = deepClone(obj)
newObj.name = '时崎狂三',
newObj.address.id = '002'

console.log('obj', obj);
console.log('newObj', newObj);

function deepClone(obj) {
   // 判断深拷贝的是数组还是对象,是对象则对象拷贝反之数组拷贝
   let newObj = Array.isArray(obj) ? [] : {}
   // 传入的obj不能为空并且要是数组或者对象,null也是对象
   if (obj && typeof obj === "object") {
       for (key in obj) {
           // hasOwnProperty()方法用于检测一个对象是否含有特定的自身属性,返回一个布尔值
           if (obj.hasOwnProperty(key)) {
               //obj里面属性值不为空并且还是对象,进行深度拷贝
               if (obj[key] && typeof obj[key] === "object") {
                   //递归进行深度的拷贝
                   newObj[key] = deepClone(obj[key])
               } else {
                   // 反之直接拷贝
                   newObj[key] = obj[key]
               }
           }
       }
   }
   return newObj
}

在这里插入图片描述

  1. 用jQuery的extend方法
const arr = ['小明', '小红', { name: '小紫', age: 20 }]
let newArr = $.extend(true,[],arr);
newArr[0] = '小绿'
newArr[2].age = 99
console.log('arr', arr); // ['小明', '小红', {name: '小紫', age: 20}]
console.log('newArr', newArr); // ['小绿', '小红', {name: '小紫', age: 99}]
  1. 用lodash函数库

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库

const objects = [{ 'a': 1 }, { 'b': 2 }];
let deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]); // => false

3.4 数组对象去重

3.4.1 用indexOf()

indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置,如果没有找到匹配的字符串则返回 -1。

const arr = [1, 2, 35, 35, '香风智乃', '香风智乃', '雏鹤爱']
let arr_2 = _set(arr)
console.log(arr_2); //  [1, 2, 35, '香风智乃', '雏鹤爱']

function _set(arr) {
   let newArr = []
   for (let i in arr) {
       if (newArr.indexOf(arr[i]) === -1) {
           newArr.push(arr[i])
       }
   }
   return newArr
}

3.4.2 用new Set()

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。

let arr_2 = [...new Set(arr)]
console.log(arr_2); //  [1, 2, 35, '香风智乃', '雏鹤爱']

3.4.3 用reduce()

reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。

let arr_3 = [
    { id: 1, name: 'obj' },
    { id: 3, name: 'string' },
    { id: 2, name: 'arr' },
    { id: 1, name: 'num' },
    { id: 1, name: 'tttt' }
]
let obj = {}
arr_3 = arr_3.reduce((pre, cur) => {
    obj[cur.id] ? '' : obj[cur.id] = true && pre.push(cur)
    return pre
}, [])
console.log('arr_3', arr_3);

3.5 扩展题

3.5.1 红绿灯

按照后端返回的arr,先打印红5s后打印黄2s后打印绿,依次循环不同的颜色

解决思路:利用异步编程方案,返回一个promise再用async、await依次接收最后递归调用重复此过程

const arr = [{ color: 'red', time: 5000 }, { color: 'yellow', time: 2000 }, { color: 'green', time: 3000 }]
const taskRunner = (color, time) => (
    new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`${time}秒后=======>${color}`)
            resolve()
        }, time);
    })
)

const task = async () => {
    for (let i in arr) {
        await taskRunner(arr[i].color, arr[i].time)
    }
    task()
}
task()

3.5.2 get方法

第一个参数是目标对象:let a = {b:{c:[1,2,3]}},第二个参数是path: b.c.2,返回结果3

解决思路:先把path传换为数组,在用while循环每一次res的属性值直至拿到结果

const obj = {b:{c:[1,2,3]}}
const path = 'b.c.3'
let result = get(obj, path)
console.log('res', result) // 3

function get(target, path) {
    let prop
    let paths = path.split('.')
    let res = target
    // prop变量每次会保存paths弹出的值,直至undefined结束循环返回结果
    while (prop = paths.shift()) {
        /* 
            res变量每次被赋值为prop属性名中的属性值
            第一轮:prop = "b", res = c: [1, 2, 3]
            第二轮:prop = "c", res = [1, 2, 3]
            第三轮:prop = "2", res = 3
            第四轮:prop = undefined
            .... 
        */
        // :第一轮: res=c:[1,2,3] 
        res = res[prop]
    }

    return res
}

3.6 数据类型

js的数据类型分为两种:基本数据类型(原始类型)、引用数据类型(对象类型),基本类型数据存放在栈中 引用数据类型存放在堆中

3.6.1 基本数据类型

Null:空值,undefined:未定义,Boolean:布尔值,Number:数字,String:字符串,Symbol:表示独一无二的值 、BigInt :表示任意大的整数。

  • null 和 undefined 的区别:在 if 语句中 null 和 undefined 都会转为false两者用相等运算符比较也是相等。undefined 代表的含义是未定义定义了形参没传实参等等;null 代表的含义是空对象。也作为对象原型链的终点主要赋值给可能会返回对象的遍历作为初始化。
  • ES10新增:BigInt 表示任意大的整数:BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值。在对大整数执行数学运算时,以任意精度表示整数的能力尤为重要。使用BigInt,整数溢出将不再是问题。

3.6.2 引用数据类型

Object、Array、 function、Date、RegExp。 JavaScript不支持创建任何自定义类型的数据,也就是说JavaScript中所有值的类型都是上面8中之一。

3.6.3 数据类型存储和堆栈内存

基本数据类型:直接存储在栈内存中,占据空间小,大小固定是属于被频繁使用的数据。
引用数据类型:将指针存储在栈中,值存储在堆中。当对象值赋值给另一个变量时,赋值的是对象的指针,指向同一块内存地址。

3.7 作用域和作用域链

3.7.1 作用域

作用域是定义变量的区域,它有一套访问变量的规则,这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找。函数内部局部作用域(函数作用域),函数外面全局作用域。

全局作用域就是Js中最外层的作用域,在哪里都可以访问。函数作用域是js通过函数创建的一个独立作用域,只能在函数内部访问,函数可以嵌套,所以作用域也可以嵌套。Es6中新增了块级作用域(由大括号包裹,比如:if(){},for(){}等)

3.7.2 作用域链

各个作用域的嵌套关系组成一条作用域链。作用域链主要是进行标识符(变量和函数)的查询,标识符解析就是沿着作用域链一级一级的搜索标识符的过程,而作用域链就是保证对变量和函数的有序访问。

如果自身作用域中声明该变量,则无需使用作用域链。如果自身作用域中未声明该变量,则需要使用作用域链进行查找。

3.8 对象、数组、字符串的方法

3.8.1 object Api


Object.is() 是一种判断两个值是否相同的方法。
语法:Object.is(value1, value2);
参数:value1:要比较的第一个值。value2:要比较的第二个值。
返回值:一个布尔表达式,指示两个参数是否具有相同的值。
 
Object.assign() 方法用于将所有可枚举的自身属性从一个或多个源对象复制到目标对象。
语法:Object.assign(target, ...sources)
参数:target:目标对象——应用源属性的对象,修改后返回。sources:源对象——包含你要应用的属性的对象。
返回值:修改后的目标对象。
 
 
Object.entries() ES8的Object.entries是把对象转成键值对数组, [key, value] 对的数组。
语法:Object.entries(obj)
参数:obj:要返回其自己的可枚举字符串键属性 [key, value] 对的对象。返回值:给定对象自己的可枚举字符串键属性 [key, value] 对的数组。
Object.fromEntries则相反,是把键值对数组转为对象
 
Object.values() 方法返回给定对象自己的可枚举属性值的数组,其顺序与 for...in 循环提供的顺序相同。
语法:Object.values(obj)
参数:obj:要返回其可枚举自身属性值的对象。返回值:包含给定对象自己的可枚举属性值的数组。
 
Object.prototype.hasOwnProperty()
hasOwnProperty() 方法返回一个布尔值,指示对象是否具有指定的属性作为它自己的属性。
如果指定的属性是对象的直接属性,则该方法返回 true — 即使值为 null 或未定义。如果该属性是继承的或根本没有声明,则返回 false语法:hasOwnProperty(prop)
参数:prop:要测试的属性的字符串名称或符号。
返回值:如果对象将指定的属性作为自己的属性,则返回true;否则为false。
 
Object.keys()
Object.keys() 方法用于返回给定对象自己的可枚举属性名称的数组,以与普通循环相同的顺序迭代。
语法:Object.keys(obj)
参数:obj:要返回可枚举自身属性的对象。
返回值:表示给定对象的所有可枚举属性的字符串数组。
 
Object.prototype.toString()
toString() 方法返回一个表示对象的字符串。当对象将被表示为文本值或以期望字符串的方式引用对象时,将自动调用此方法 id。默认情况下,toString() 方法由从 Object 继承的每个对象继承。
语法:toString()
返回值:表示对象的字符串。
 
Object.freeze()
Object.freeze() 方法冻结一个对象,这意味着它不能再被更改。冻结对象可防止向其添加新属性,防止删除现有属性,防止更改现有属性的可枚举性、可配置性或可写性,并防止更改现有属性的值。它还可以防止其原型被更改。
语法:Object.freeze(obj)
参数:obj:要冻结的对象。返回值:传递给函数的对象。
 
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 (请打开浏览器控制台以查看运行结果。)
语法:const me = Object.create(person);
参数:
proto:新创建对象的原型对象。
propertiesObject
可选。需要传入一个对象,该对象的属性类型参照Object.defineProperties()的第二个参数。如果该参数被指定且不为 undefined,该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。
返回值
一个新对象,带着指定的原型对象和属性。

3.8.2 Array Api


1、sort( ):sort 排序 如果下面参数的正反 控制 升序和降序 ,返回的是从新排序的原数组
2、splice( ):向数组的指定index处插入 返回的是被删除掉的元素的集合,会改变原有数组;截取类 没有参数,返回空数组,原数组不变;一个参数,从该参数表示的索引位开始截取,直至数组结束,返回截取的 数组,原数组改变;两个参数,第一个参数表示开始截取的索引位,第二个参数表示截取的长度,返回截取的 数组,原数组改变;三个或者更多参数,第三个及以后的参数表示要从截取位插入的值。会改变原数据
3、pop( ):从尾部删除一个元素 返回被删除掉的元素,改变原有数组。
4、push( ):向数组的末尾追加 返回值是添加数据后数组的新长度,改变原有数组。
5、unshift( ):向数组的开头添加 返回值是添加数据后数组的新长度,改变原有数组。
6、shift( ):从头部删除一个元素 返回被删除掉的元素,改变原有数组。
7、reverse( ): 原数组倒序  它的返回值是倒序之后的原数组
8、concat( ):数组合并。
9、slice( ):数组元素的截取,返回一个新数组,新数组是截取的元素,可以为负值。从数组中截取,如果不传参,会返回原数组。如果只传入一个参数,会从头部开始删除,直到数组结束,原数组不会改变;传入两个参数,第一个是开始截取的索引,第二个是结束截取的索引,不包含结束截取的这一项,原数组不会改变。最多可以接受两个参数。
10、join( ):讲数组进行分割成为字符串  这能分割一层在套一层就分隔不了了
11、toString( ):数组转字符串;
12、toLocaleString( ):将数组转换为本地数组。
13、forEach( ):数组进行遍历;
14、map( ):没有return时,对数组的遍历。有return时,返回一个新数组,该新数组的元素是经过过滤(逻辑处理)过的函数。
15、filter( ):对数组中的每一运行给定的函数,会返回满足该函数的项组成的数组。
16、every( ):当数组中每一个元素在callback上被返回true时就返回true。(注:every其实类似filter,只不过它的功能是判断是不是数组中的所有元素都符合条件,并且返回的是布尔值)。
17、some( ):当数组中有一个元素在callback上被返回true时就返回true。(注:every其实类似filter,只不过它的功能是判断是不是数组中的所有元素都符合条件,并且返回的是布尔值)。
18、reduce( ):回调函数中有4个参数。prev(之前计算过的值),next(之前计算过的下一个的值),index,arr。把数组列表计算成一个
19.isArray() 判断是否是数组
20. indexOf  找索如果找到了就会返回当前的一个下标,若果没找到就会反回-1
21. lastIndexOf 它是从最后一个值向前查找的 找索如果找到了就会返回当前的一个下标,若果没找到就会反回-1
22. Array.of() 填充单个值
23. Array.from() 来源是类数组    
24.fill填充方法 可以传入3各参数 可以填充数组里的值也就是替换 如果一个值全部都替换掉 ,    第一个参数就是值 第二个参数 从起始第几个 第三个参数就是最后一个
find  查找这一组数 符合条件的第一个数 给他返回出来
findIndex() 查找这一组数 符合条件的第一数的下标 给他返回出来     没有返回 -1  
keys 属性名  values属性值  entries属性和属性值
forEach 循环遍历 有3个参数 无法使用 break continue , 参数一就是每个元素 参数二就是每个下标 参数三就是每个一项包扩下标和元素
 
 
### 改变数组本身的api
1. `pop()`  尾部弹出一个元素
2. `push()` 尾部插入一个元素
3. `shift()`  头部弹出一个元素
4. `unshift()`  头部插入一个元素
5. `sort([func])` 对数组进行排序,func有2各参数,其返回值小于0,那么参数1被排列到参数2之前,反之参数2排在参数1之前
6. `reverse()` 原位反转数组中的元素
7. `splice(pos,deleteCount,...item)`  返回修改后的数组,从pos开始删除deleteCount个元素,并在当前位置插入items
8. `copyWithin(pos[, start[, end]])` 复制从start到end(不包括end)的元素,到pos开始的索引,返回改变后的数组,浅拷贝
9. `arr.fill(value[, start[, end]])` 从start到end默认到数组最后一个位置,不包括end,填充val,返回填充后的数组
其他数组api不改变原数组
 
 
 
 
map 映射关系的数组  map 主要就是有返回值可以return 数组   判断的会返回boolean 
1、map()方法返回一个新数组,新数组中的元素为原始数组中的每个元素调用函数处理后得到的值。
2、map()方法按照原始数组元素顺序依次处理元素。
 
注意:
map()不会对空数组进行检测。
map()不会改变原始数组。
map() 函数的作用是对数组中的每一个元素进行处理,返回新的元素。
filter 满足条件的都能返回 是一个数组
some返回boolean 循环数组 只要有一个成员通过了就会返回 true 反而 false
every返回boolean 循环数组 只有全部成员通过了就会返回 true 反而 false 
reduce() 累加器 把上一次计算的值,给下一次计算进行相加
set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用  
delete  [1] delete 可以删除数组中的一向
**Array.isArray()** 用于确定传递的值是否是一个 [`Array`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array)。
flat  扁平化 将嵌套的数组 “拉平”,变成一维数组。该方法返回一个新数组,对原数据没有影响。// 参数写的就是代表要扁平到第几层
 
 
 
//1、every()
var arr = [1,56,80,5];
var main = arr.every(n => n > 0);
console.log(main)   //输出:true
 
//2、some()
var arr = [1,-56,80,-5];
var main = arr.some(n => n > 0);
console.log(main)    //输出:true
 
//3、reducer()
var arr = [10,20,30,40]
let result = arr.reduce(function(prev,next,index,arr){
	return prev + next;
})
console.log(result);  //输出:100
 
// 4、filter  返回满足要求的数组项组成的新数组
var arr3 = [3,6,7,12,20,64,35]
var result3 = arr3.filter((item,index,arr)=>{
    return item > 3
})
console.log(result3)  //[6,7,12,20,64,35]
 
// 5、map  返回每次函数调用的结果组成的数组
var arr4 = [1,2]
var result4 = arr4.map((item,index,arr)=>{
    return `<span>${item}</span>`
})
console.log(result4)  
/*[ '<span>1</span>',
  '<span>2</span>', ]*/
 
 
ES6数组的常用方法:
 
1、Array.from( ):将对象或字符串转成数组,注意得有length。
2、Array.of( ): 将一组值转换为数组。
3、copyWithin(target,start(可选),end(可选)):数组内数据的复制替换
	target:从该位置开始替换数据;
	start:从该位置开始读取数据,默认为0;
	end:到该位置停止数据的读取,默认为数组的长度
4、find( ):用于找出第一个符合条件的数组成员。
5、findIndex( ):返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-16、fill(value,start,end):使用给定值,填充一个数组。
	value:填充的值;
	start:开始填充的位置;
	end:填充结束的位置。
7、keys( ):对键名的遍历。
8、values( ):对键值的遍历。
9、entries( ):对键值对的遍历。
10、includes( ):数组原型的方法,查找一个数值是否在数组中,只能判断一些简单类型的数据,对于复杂类型的数据无法判断。该方法接受两个参数,分别是查询的数据和初始的查询索引值。
11、flat( ):用于数组扁平,数组去除未定义。可以去除空项。
12、flatMap( ):对原数组的每个成员执行一个函数。
13、Map( ):是一组键值对的结构,具有极快的查找速度。
14、Set( ):Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
 
 
//1、Array.from()  --   Array.of()
	var  arrayLink = {
		"0":"a",
		"1":"b",
		"2":"c",
		length:3
	}
	var arr = Array.from(arrayLink)
	console.log(arr)   // 输出: [a,b,c]
	console.log(Array.from("abcdefg"))  //输出:["a", "b", "c", "d", "e", "f", "g"]
	console.log(Array.of(1,2,3,4,5))  //输出: [1, 2, 3, 4, 5]
 
//2、copyWithin()
	var arr = [1,2,3,4,5];
	var main = arr.copyWithin(0,3);
	console.log(main);   //输出:[4,5,3,4,5]
 
//3、find()
	var arr = [1,-5,2,9,-6];
	var main = arr.find(n =>  n < 0);
	console.log(main);   //输出:-5
 
//4、fill()
	var arr = ["a","b","c","d"];
	console.log(arr.fill(7,1,2));//输出:["a",7,"c","d"]  
 
//5、keys()  values()  entries()
	var arr = ["a","b","c","d"];
	for(let index of arr.keys()){
		console.log(index);
	}
	for(let elem of arr.values()){
		console.log(elem);
	}
	for(let [index,elem] of arr.entries()){
		console.log(index,elem);
	}  
 
//6、includes()
	let arr = [12,34,223,45,67]
	console.log(arr.includes(45))   //输出:true
	[1, 2, NaN].includes(NaN)     // true
	[1, 2, NaN].indexOf(NaN)      // -1
 
//7、Map
	var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
	m.get('Michael'); // 95
	//初始化Map需要一个二维数组,或者直接初始化一个空Map。Map具有以下方法:
	var m = new Map(); // 空Map
	m.set('Adam', 67); // 添加新的key-value
	m.set('Bob', 59);
	m.has('Adam'); // 是否存在key 'Adam': true
	m.get('Adam'); // 67
	m.delete('Adam'); // 删除key 'Adam'
	m.get('Adam'); // undefined
	//由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
	var m = new Map();
	m.set('Adam', 67);
	m.set('Adam', 88);
	m.get('Adam'); // 88
 
//8、Set
	//要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
	var s1 = new Set(); // 空Set
	var s2 = new Set([1, 2, 3]); // 含1, 2, 3
	//重复元素在Set中自动被过滤:
	var s = new Set([1, 2, 3, 3, '3']);
	s; // Set {1, 2, 3, "3"}  注意:数字3和字符串'3'是不同的元素
	//通过add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果:
	s.add(4);
	s; // Set {1, 2, 3, 4}
	s.add(4);
	s; // 仍然是 Set {1, 2, 3, 4}
	//通过delete(key)方法可以删除元素:
	var s = new Set([1, 2, 3]);
	s; // Set {1, 2, 3}
	s.delete(3);
	s; // Set {1, 2}

3.8.3 String Api

1、chartAt( ):返回在指定位置的字符;
2、concat( ):返回新的字符串**,将一个或多个字符串与原字符串连接合并
3、indexOf( ):检索字符串,返回第一次出现的索引,没有出现则为-1
4、lastIndexOf(searchValue[ fromIndex]) 返回从字符串尾部开始第一次出现的索引,没有则-1,fromIndex的值相对于从尾部开始的索引
5、split( ):返回一个以指定分隔符出现位置分隔而成的一个数组,数组元素不包含分隔符
6、substr( ):从起始索引号提取字符串中指定数目的字符;
7、substring( ):提取字符串中两个指定的索引号之间的字符;
8、toLowerCase( ):字符串转小写;
9、toUpperCase( ):字符串转大写;
10、valueOf( ):返回某个字符串对象的原始值; 
11、trim( ):删除字符串两边的空格;
12、trimeState 取出开始的空格
13、trimeEnd  去除末尾空格
14、includes(searchString[, position])返回boolean,判断一个字符串是否包含在另一个字符串中,从postition索引开始搜寻,默认0
15、slice( ):提取字符串片段,并在新的字符串中返回被提取的部分;
16、search(regexp)返回首次匹配到的索引,没有则-1,执行正则表达式和 String 对象之间的一个搜索匹配
17、toString()返回一个表示调用对象的字符串,该方法返回指定对象的字符串形式
18、trim()返回去掉两端空白后的新字符串 还有trimend trimstart
19、replace() 把指定的字符串替换成为别的字符

3.9 this指向

在全局的环境下this是指向window 的普通函数调用直接调用中的this 会指向 window, 严格模式下this会指向 undefined,自执行函数 this 指向 window,定时器中的 this 指向 window

在对象里调用的this,指向调用函数的那个对象,在构造函数以及类中的this,构造函数配合 new 使用, 而 new 关键字会将构造函数中的 this 指向实例化对象,所以构造函数中的 this 指向 当前实例化的对象。

在方法中的this谁调用就指向谁。箭头函数没有自己的 this,箭头函数的this在定义的时候,会继承自外层第一个普通函数的this

3.10 闭包

  • 闭包的概念就是:只有权利访问另一个函数作用域中的变量,一般就是函数包裹着函数。

  • 闭包可以重用一个变量,且保证这个变量不会被污染的一种机制。这些变量的值始终保持在内存中,不会被垃圾回收机制处理。

  • 闭包的缺点:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

  • 为什么要用闭包:使用场景 : 防抖、节流、函数套函数避免全局污染

闭包原理
函数执行分成两个阶段(预编译阶段和执行阶段)1.在预编译阶段,如果发现内部函数使用了外部函数的变量,则会在内存中创建一个“闭包”对象并保存对应变量值,
    如果已存在“闭包”,则只需要增加对应属性值即可。
    
    2.执行完后,函数执行上下文会被销毁,函数对“闭包”对象的引用也会被销毁,但其内部函数还持用该“闭包”的引用,
    所以内部函数可以继续使用“外部函数”中的变量
​
利用了函数作用域链的特性,一个函数内部定义的函数会将包含外部函数的活动对象添加到它的作用域链中,函数执行完毕,其执行作用域链销毁,
但因内部函数的作用域链仍然在引用这个活动对象,所以其活动对象不会被销毁,直到内部函数被烧毁后才被销毁。

3.11 call、apply、bind

都是改变this指向和函数调用,实际上call与apply的功能是一样的比如可以调用父构造函数来实现继承等等,但两者传参不一样。
call方法第二个参数是参数列表arg1, arg2, …。
apply方法第二个参数是一个数组[arg1, arg2, …]。
其上两个方法使用后可直接调用。 bind 传参后不会立即执行,而是返回一个改变了this指向的函数,这个函数可以继续传参,且执行,需要类似于bind()()两个括号才能调⽤。

3.12 函数柯里化

3.12.1 概念

把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

3.12.2 特点

  • 接收单一参数,将更多的参数通过回调函数来搞定
  • 返回一个新函数,用于处理所有的想要传入的参数
  • 需要利用call/apply与arguments对象收集参数
  • 返回的这个函数正是用来处理收集起来的参数。

3.12.3 用途

函数柯里化是对闭包的一种应用形式,延迟计算、参数复用、动态生成函数(都是闭包的用途)。

3.12.4 例子

柯里化函数:把一个多参数的函数转化为单参数函数的方法。并且返回接受余下的参数而且返回结果的新函数的技术。

// 简单的相加函数
var add = function (x,y) {
    return x + y
}
// 调用:
add(1,2)// 柯里化以后
var add = function (x) { //柯里化函数(闭包)
    return function (y) {
        return x + y
    }
}
add(1)(2)

3.13 高阶函数

接收函数作为参数或者返回函数的函数


function higherOrderFunction(param,callback){
    return callback(param);
}

3.14 new的原理

首先在堆内存中开辟一个空间

  1. 创建一个空的简单 JavaScript 对象(即 {})
  2. 为步骤 1 新创建的对象添加属性 proto,将该属性链接至构造函数的原型对象
  3. 将步骤 1 新创建的对象作为 this 的上下文
  4. 如果该函数没有返回对象,则返回 this
function _new(fn,...args){   // ...args为ES6展开符,也可以使用arguments
    //先用Object创建一个空的对象,
    const obj = Object.create(fn.prototype)  //fn.prototype代表 用当前对象的原型去创建
    //现在obj就代表Dog了,但是参数和this指向没有修改
    const rel = fn.apply(obj,args)
    //正常规定,如何fn返回的是null或undefined(也就是不返回内容),我们返回的是obj,否则返回rel
    return rel instanceof Object ? rel : obj
}

3.15 垃圾回收机制和内存机制

3.15.1 垃圾回收

浏览器的js具有自动垃圾回收机制,垃圾回收机制也就是自动内存管理机制,垃圾收集器会定期的找出那些不在继续使用的变量,然后释放内存。但是这个过程不是实时的,因为GC开销比较大并且时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。

标记清除:大部分浏览器使用这种垃圾回收,当变量进入执行环境(声明变量)的时候,垃圾回收器将该变量进行了标记,当该变量离开环境的时候,将其再度标记,随之进行删除。

引用计数:这种方式常常会引起内存的泄露,主要存在于低版本的浏览器。它的机制就是跟踪某一个值得引用次数,当声明一个变量并且将一个引用类型赋值给变量得时候引用次数加1,当这个变量指向其他一个时引用次数减1,当为0时出发回收机制进行回收。

3.15.2 内存泄漏

如果 那些不再使用的变量,它们所占用的内存 不去清除的话就会造成内存泄漏。
内存泄露其实就是我们的程序中已经动态分配的堆内存,由于某些原因没有得到释放,造成系统内存的浪费导致程序运行速度减慢甚至系统崩溃等严重后果。

比如说:

  • 全局变量
  • 闭包:在闭包中引入闭包外部的变量时,当闭包结束时此对象无法被垃圾回收(GC)。
  • DOM:当原有的DOM被移除时,子结点引用没有被移除则无法回收
  • Times计时器泄露

3.16 事件循环(event Loop)

由于js是单线程语言,所以任务是一个一个顺序执行的。如果一个任务耗时过长,那么后面的一个任务就必须等待。那么问题来了,假如浏览网站时图片资源很大加载的很慢,难道网页要一直卡到图片完全显示出来?所以js的任务分为了两类同步任务和异步任务。而异步任务又分为宏任务和微任务,因此js执行的过程为:同步任务->微任务->宏任务

在这里插入图片描述

  1. 先执行所有同步任务,碰到异步任务放到任务队列中
  2. 同步任务执行完毕,开始执行当前所有的异步任务
  3. 先执行任务队列里面所有的微任务
  4. 然后执行一个宏任务
  5. 然后再执行所有的微任务
  6. 再执行一个宏任务,再执行所有的微任务·······依次类推到执行结束。

3-6的这个循环称为事件循环Event Loop。
事件循环是JavaScript实现异步的一种方法,也是JavaScript的执行机制。

3.16.1 同步任务

在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务。new promise()、console.log()属于同步任务。

3.16.2 异步任务

不进入主线程、而进入"任务队列"的任务;只有等主线程任务全部执行完毕,"任务队列"的任务才会进入主线程执行。

在这里插入图片描述

js异步有一个机制,遇到宏任务先执行宏任务,将宏任务放入eventqueue,然后在执行微任务,将微任务放入eventqueue里。但这两个queue不是同一个。当调用时先从微任务里拿这个回调函数,然后再从宏任务里的queue中拿回调函数。

  • 宏任务:包括整体代码script(可以理解为外层同步代码),setTimeout,setInterval、setImmediate、还有如 I/O 操作、UI 渲染等。
  • 微任务:原生Promise.then、process.nextTick、Object.observe(已废弃)、对 Dom 变化监听的 MutationObserver。

3.17 延迟加载

JavaScript 是单线程(js不走完下面不会走是因为同步)会阻塞DOM的解析,因此也就会阻塞DOM的加载。所以有时候我们希望延迟JS的加载来提高页面的加载速度。

  • 把JS放在页面的最底部
  • script标签的defer属性:脚本会立即下载但延迟到整个页面加载完毕再执行。该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)。
  • 是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行,标记为async的脚本并不保证按照指定他们的先后顺序执行, 该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)。
  • 动态创建script标签,监听dom加载完毕再引入js文件。

3.18 预解析

4. TS

待更新中…

二、框架

1. Vue

vue是一套构建于用户界面的自底向上方 增量开发 渐进式基于MVVM的框架核心思想是数据驱动,组件化。数据驱动:视图内容根据数据的改变而改变组件化:增加代码复用性,可维护性,可测试性,提高开发效率,方便重复使用,体现了高内聚,低耦合。

1.1 优点

  • 轻量级:只关注view视图层,是一个构建数据的视图结合,大小只有几十kb。
  • 双向数据绑定:保留了的angular的特点,在data()中定义的变量利用插值表达式{{}}在模板中使用,一旦变量改变模板也随之改变。
  • 组件化:保留了react的优点,实现了组件之前的封装和重用也以便于后期维护和扩展
  • 虚拟DOM:原生的DOM比较庞大属性也很多所以操作起来比较耗费性能,所以vue引入了VNode来记录页面的变化并映射DOM,最后再反映到页面上。

1.2 MVVM、MVC、MVP模型

  • mvvm:由Model、View、ViewModel三部分组成。Model代表数据模型;View代表UI组件负责将数据模型转化成页面展现出;ViewModel通过双向数据绑定把View层和Model层连接起来。因此只需关注业务逻辑也不需要手动操作DOM。在这里插入图片描述

  • mvc:由Model、View、Controller三部分组成。Model代表数据模型;View代表模型包含的数据的可视化;Controller控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。比如python的Django就是典型的mvc模型。在这里插入图片描述

  • mvp:由Model、View、Presenter三部分组成。Model代表数据模型;View代表数据转化的网页、Presenter的功能与controller相同,也是负责连接view与model,但它可以俩俩进行双向通信的在这里插入图片描述

1.3 数据双向绑定

vue采用数据劫持结合发布-订阅者模式的方式,通过Object.defineProperty()来给各个属性添加setter、getter并劫持监听,在数据变动时发布消息给订阅者,触发相应的监听回调。

1、数据监听器Observer给data中的数据进行递归遍历,包括子属性对象的属性都加上setter和getter进行监听,若有变动就拿到最新的值通知订阅者。
2、指令解析器Compile对模板中每个元素节点的指令进行扫描和解析,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者一旦数据有变动收到通知更新视图。
3、订阅者Wathcer是Observer和Compile之间通信的桥梁,能够订阅并收到每个属性变动的通知。在自身实例化时往属性订阅器(dep)里添加自己,且必须有一个update()方法待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
4、MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新。

在这里插入图片描述

1.4 响应式原理

在数据发生改变时视图会重新渲染匹配更新为最新的值。Object.defineProerty为data对象中的每一个属性,设置get和set方法,每一个被声明的属性都会有一个专属的依赖收集器subs,当页面使用到某个属性时,触发Object.defineProerty的get函数,页面的watcher就会被放到属性依赖收集器subs中在数据变化时通知更新;当数据改变时,触发Object.defineProperty 的set函数,数据会遍历自己的 依赖收集器 subs,逐个通知 watcher,视图开始更新。

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty(obj, prop, descriptor) —— obj:要定义属性的对象;prop:要定义或修改的属性的名称或 Symbol ;descriptor:要定义或修改的属性描述符。

1.5 生命周期

  • beforeCreate(创建前):vue实例挂载的元素$el和数据对象data都是undefined还未初始化,在此阶段可以使用loading事件
  • created(创建后):vue实例的数据对象data有了,$el还没有更新,在此阶段可以发ajax请求。
  • bedoreMount(渲染前):vue实例的$el和data都初始化了,但还是虚拟dom节点。
  • mounted(渲染后):vue实例挂载完成,真实dom成功渲染到模板中,在此阶段可以使用路有钩子。
  • beforeUpdate(更新后):在data更新时触发。
  • updated(更新后):在data更新时触发,在此阶段可以使用watch进行观测。
  • beforeDestroy(销毁前):组件将要销毁时触发。
  • destroyed(销毁后):组件销毁时触发,vue实例解除了事件监听以及和dom的绑定(无响应了),但DOM节点依旧存在。

父子组件生命周期顺序

  • 创建、挂载阶段:父beforeCreate→ 父created→ 父beforeMounte→ 子beforCreate→ 子created→ 子beforeMount→ 父mounted。
  • 组件更新:父beforeUpdate→ 子beforeUpdate→ 子updated→ 父updated。
  • 组件销毁:父beforeDestroy→ 子beforeDestroy→ 子destroyed→ 父destroyed。

在这里插入图片描述

1.6 内置指令

  • v-text:更新元素的 textContent。如果要更新部分的 textContent,需要使用 {{ Mustache }} 插值。
  • v-html:更新元素的 innerHTML。注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。如果试图使用 v-html 组合模板,可以重新考虑是否通过使用组件来替代。
  • v-show:根据表达式之真假值,切换元素的 display CSS property。
  • v-if:根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是 ,将提出它的内容作为条件块。

当和 v-if 一起使用时,v-for 的优先级比 v-if 更高

  • v-for: 遍历数组,渲染数据
  • v-on:绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。

修饰符:

  • .stop - 调用 event.stopPropagation()。
  • prevent - 调用 event.preventDefault()。
  • .capture - 添加事件侦听器时使用 capture 模式。
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
  • .native - 监听组件根元素的原生事件。
  • .once - 只触发一次回调。
  • .left - (2.2.0) 只当点击鼠标左键时触发。
  • .right - (2.2.0) 只当点击鼠标右键时触发。
  • .middle - (2.2.0) 只当点击鼠标中键时触发。
  • .passive - (2.3.0) 以 { passive: true } 模式添加侦听器
  • v-bind:动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。在绑定 class 或 style attribute 时,支持其它类型的值,如数组或对象

修饰符:

  • .prop - 作为一个 DOM property 绑定而不是作为 attribute 绑定。
  • .camel - (2.1.0+) 将 kebab-case attribute 名转换为 camelCase。
  • .sync (2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器。
  • v-model:在表单控件或者组件上创建双向绑定。

修饰符:

  • .lazy - 取代 input 监听 change 事件。
  • .number - 输入字符串转为有效的数字。
  • .trim - 输入首尾空格过滤’
  • v-slot:提供具名插槽或需要接收 prop 的插槽。
  • v-pre:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
  • v-cloak:这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
  • v-once:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

1. 7 自定义指令

自定义指令是用来操作DOM的。尽管Vue推崇数据驱动视图的理念,但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅可用于定义任何的DOM操作,并且是可复用的。

1.7.1 用法

  • 全局指令:通过 Vue.directive() 函数注册一个全局的指令。
Vue.directive("focus", {
    inserted: function(el){
         el.focus();
     }
 })
  • 局部指令:通过在Vue实例中添加 directives 对象数据注册局部自定义指令。
directives: {
    focus: {
         inserted: function(el){
             el.focus();
         }
     }
}

1.7.2 钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用。可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用。
  • undate:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind:只调用一次,指令与元素解绑时调用。

1.7.3 钩子的参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值。
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子 中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。
    • arg:传给指令的参数。
    • modifiers:一个包含修饰符的对象。
    • vnode:Vue 编译生成的虚拟节点。
    • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

1.7.4 案例

在图片未完成加载前,用随机的背景色占位,图片加载完成后才直接渲染出来

<div id="app" v-image = "item " v-for="item in imageList"></div>
<script>
Vue.directive("image", {
    inserted: function(el,binding){
         var color = Math.floor(Math,random()*1000000)
         el.style.backgroundColor = "#" + color
         var img = new Image()
         img.src = binding.vaule
         img.onload = function(){ 
           el.style.backgroundImage =  “url(+ binding.vaule + ")"
         }
     }
 })
 new Vue({
        el: "#app",
        data: {
            imageList: [
                {
                    url: "http://consumer-img.huawei.com/content/dam/huawei-cbg-site/greate-china/cn/mkt/homepage/section4/home-s4-p10-plus.jpg"
                },
                {
                    url: "http://consumer-img.huawei.com/content/dam/huawei-cbg-site/greate-china/cn/mkt/homepage/section4/home-s4-watch2-pro-banner.jpg"
                },
                {
                    url: "http://consumer-img.huawei.com/content/dam/huawei-cbg-site/en/mkt/homepage/section4/home-s4-matebook-x.jpg"
                }
            ]
        }
    })
 </script>

1. 8 组件通信

  • props和$emit
    父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的。
Vue.component('child',{
  data(){
    return {
      mymessage:this.message
    }
  },
  template:`
    <div>
      <input type="text" v-model="mymessage" @input="passData(mymessage)"> </div>
  `,
  props:['message'],//设置props属性值,得到父组件传递过来的数据
  methods:{
    passData(val){
      //触发父组件中的事件,向父组件传值
      this.$emit('getChildData',val)
    }
  }
})
Vue.component('parent',{
  template:`
    <div>
      <p>this is parent compoent!</p>
      <child :message="message" v-on:getChildData="getChildData"></child>
    </div>
  `,
  data(){
    return {
      message:'hello'
    }
  },
  methods:{
    //执行子组件触发的事件
    getChildData(val){
      console.log(val)
    }
  }
})

prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。

  • $attrs和$listeners
    多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----$attrs/$listeners。
    • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=“$attrs” 传入内部组件。通常配合 inheritAttrs 选项一起使用。
    • $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件。
Vue.component('C',{
  template:`
    <div>
      <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)"> </div>
  `,
  methods:{
    passCData(val){
      //触发父组件A中的事件
      this.$emit('getCData',val)
    }
  }
})
Vue.component('B',{
  data(){
    return {
      mymessage:this.message
    }
  },
  template:`
    <div>
      <input type="text" v-model="mymessage" @input="passData(mymessage)">
      <!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
      <!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
      <C v-bind="$attrs" v-on="$listeners"></C>
    </div>
  `,
  props:['message'],//得到父组件传递过来的数据
  methods:{
    passData(val){
      //触发父组件中的事件
      this.$emit('getChildData',val)
    }
  }
})
Vue.component('A',{
  template:`
    <div>
      <p>this is parent compoent!</p>
      <B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
    </div>
  `,
  data(){
    return {
      message:'hello',
      messagec:'hello c' //传递给c组件的数据
    }
  },
  methods:{
    getChildData(val){
      console.log('这是来自B组件的数据')
    },
    //执行C子组件触发的事件
    getCData(val){
      console.log("这是来自C组件的数据:"+val)
    }
  }
})

$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

  • eventBus
    又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
Vue.component('brother1',{
  data(){
    return {
      mymessage:'hello brother1'
    }
  },
  template:`
    <div>
      <p>this is brother1 compoent!</p>
      <input type="text" v-model="mymessage" @input="passData(mymessage)">
    </div>
  `,
  methods:{
    passData(val){
      //触发全局事件globalEvent
      bus.$emit('globalEvent',val)
    }
  }
})
Vue.component('brother2',{
  template:`
    <div>
      <p>this is brother2 compoent!</p>
      <p>brother1传递过来的数据:{{brothermessage}}</p>
    </div>
  `,
  data(){
    return {
      mymessage:'hello brother2',
      brothermessage:''
    }
  },
  mounted(){
    //绑定全局事件globalEvent
    bus.$on('globalEvent',(val)=>{
      this.brothermessage=val;
    })
  }
})
//中央事件总线
var bus=new Vue();
var app=new Vue({
  el:'#app',
  template:`
    <div>
      <brother1></brother1>
      <brother2></brother2>
    </div>
  `
})

eventBus也有不方便之处, 当项目较大,就容易造成难以维护的灾难。

  • provide和inject
    父组件中通过provide来提供变量,然后在子组件中通过inject来注入变量,不论子组件有多深,只要调用了inject那么就可以注入provider中的数据,而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
Vue.component('child',{
    inject:['for'],//得到父组件传递过来的数据
    data(){
      return {
        mymessage:this.for
      }
    },
    template:`
      <div>
        <input type="tet" v-model="mymessage">
      </div> `
  })
  Vue.component('parent',{
    template:`
      <div>
        <p>this is parent compoent!</p>
        <child></child>
      </div>
    `,
    provide:{
      for:'test'
    },
    data(){
      return {
        message:'hello'
      }
    }
  })

provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。实现响应式的方法如下:

  • provide祖先组件的实例,然后在子孙组件中注入依赖,这样就可以在子孙组件中直接修改祖先组件的实例的属性,不过这种方法有个缺点就是这个实例上挂载很多没有必要的东西比如props,methods
  • 使用2.6最新API Vue.observable 优化响应式 provide(推荐)
Vue.component('child',{
    inject:['for'],//得到父组件传递过来的数据
    data(){
      return {
        mymessage:this.for
      }
    },
    template:`
      <div>
        <input type="tet" v-model="mymessage">
      </div> `
  })
  Vue.component('parent',{
    template:`
      <div>
        <p>this is parent compoent!</p>
        <button @click="() => changeMessage()">message</button>
        <child></child>
      </div>
    `,
    provide() {
    	return {
			for: this  //方法一:提供祖先组件的实例
		}
	},
	 // 方法二:使用2.6最新API Vue.observable 优化响应式 provide
	// provide() {
	 //   this.for= Vue.observable({
	 //     msg: "hello"
	 //   });
	 //   return {
	 //     for: this.for
	 //   };
	 // },
    data(){
      return {
        message:'hello' 
      }
    },
     methods: {
	    changeMessage(msg) {
	      if (msg) {
	        this.message= msg;
	      } else {
	        this.message= this.message=== "hello" ? "red" : "blue";
	      }
	    }
  	}
  })
  • $parent / $children与 ref / refs
    • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。
    • -$parent / $children:访问父 / 子实例。如在#app上拿$parent得到的是new Vue()的实例,在这实例上再拿$parent得到的是undefined,而在最底层的子组件拿$children是个空数组。$children 的值是数组,而$parent是个对象。
/***********ref*****************/
// component-a 子组件
export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}


// 父组件
<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.title);  // Vue.js
      comA.sayHello();  // 弹窗
    }
  }
</script>
/***********$parent / $children*****************/
// 父组件中
<template>
  <div class="hello_world">
    <div>{{msg}}</div>
    <com-a></com-a>
    <button @click="changeA">点击改变子组件值</button>
  </div>
</template>
 
<script>
import ComA from './test/comA.vue'
export default {
  name: 'HelloWorld',
  components: { ComA },
  data() {
    return {
      msg: 'Welcome'
    }
  },
 
  methods: {
    changeA() {
      // 获取到子组件A
      this.$children[0].messageA = 'this is new value'
    }
  }
}




// 子组件中
<template>
  <div class="com_a">
    <span>{{messageA}}</span>
    <p>获取父组件的值为:  {{parentVal}}</p>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      messageA: 'this is old'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}

上面两种方式用于父子组件之间的通信, 而使用props进行父子组件通信更加普遍; 二者皆不能用于非父子组件之间的通信。

  • vuex
    vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上。
    在这里插入图片描述

    • dispatch:操作行为触发方法,是唯一能执行action的方法。
    • actions:操作行为处理模块,由组件中的$store.dispatch(‘action 名称’, data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。
    • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
    • mutations:状态改变操作方法,由actions中的commit(‘mutation 名称’)来触发。是Vuex修改state的唯一推荐方法。
    • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。
    • getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
  • v-model
    父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(“input”,val)自动修改v-model绑定的值

Vue.component('child',{
    props:{
      value:String, //v-model会自动传递一个字段为value的prop属性
    },
    data(){
      return {
        mymessage:this.value
      }
    },
    methods:{
      changeValue(){
        this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值
      }
    },
    template:`
      <div>
        <input type="text" v-model="mymessage" @change="changeValue">
      </div> `
  })
  Vue.component('parent',{
    template:`
      <div>
        <p>this is parent compoent!</p>
        <p>{{message}}</p>
        <child v-model="message"></child>
      </div>
    `,
    data(){
      return {
        message:'hello'
      }
    }
  })
  var app=new Vue({
    el:'#app',
    template:`
      <div>
        <parent></parent>
      </div>
    `
  })

2. React

待更新中…

3. Uni-app

待更新中…

总结

程序员的生活就是活到老,学到老。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到