面经视频笔记

103 阅读13分钟
// 预编译
// 作用域的创建阶段 预编译的阶段
// 预编译的时候做了哪些事情
// js的变量对象 AO对象 供js引擎自己去访问的
// 1.创建了ao对象
// 2.找形参和变量的声明 作为ao对象的属性名 值是undefined 
// 3.实参和形参相统一
// 4.找函数声明 会覆盖变量的声明

const { default: Vue } = require("vue")

函数作为对象的方法被调用,谁调用我 我就指向谁

箭头函数中的this
箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。
箭头函数中,this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,
导致内部的this就是外层代码块的this。正是因为没有this所以他不能用作构造函数
箭头函数中的this是在定义函数的时候绑定
var x=11;
var obj={
    x:22,
    say:()=>{
        console.log(this.x);
    }
}
obj.say();
这里的thiswindow因为箭头函数和say平级,也就是箭头函数本身所在的对象为obj所以obj的父执行上下文是window
因此this表示的是window->输出11
所谓的定义时候绑定就是this是继承自父执行上下文中的this

var obj={
    birth:2000,
    getAge:function(){
        var b=this.birth;
        var fn=()=>{
           return this.birth;
           这里this指的是obj

        }
        return fn();
    }
};
obj.getAge();

深浅拷贝:
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型拷贝的就是基本类型的值,如果属性是引用类型拷贝的就是内存地址,如果其中一个对象改变了这个地址就会影响另一个对象
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,
且修改新对象不会影响原对象

浅拷贝和赋值的区别:
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的地址(对象是存在堆内存的
普通的变量就存在栈内存中,栈内存中只有对象名及其引用值)。也就是两个对象指向的是同一个存储看见,
无论哪个对象发送改变,其实都是改变的存储空间的内容,因此两个对象是联动的。
浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后的对象的引用累心因共享同一块内存,会相互影响
深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。
因为拷贝嘛所以一定是copy一个对象所以是从堆中创建内存

浅拷贝的实现方式:
Object.assign()
lodash里面的_.clone
...展开运算符
Array.prototype.concat
Array.prototype.slice

深拷贝的实现方式:
JSON.parse(JSON.stringify())
递归的操作
cloneDeep
Jquery.extend()

function shallowCopy(obj){
    var target={}
    for(var i in obj){
        if(obj.hasOwnProperty(i)){
            target[i]=obj[i]
        }
    }
    return target
}

function deepClone(obj){
    var cloneObj=new obj.constructor()
    if(obj==null)return obj
    if(obj instanceof Date)return new Date(obj);
    if(obj instanceof RegExp)return new RegExp(obj);
    if(typeof obj !=='object')return obj
    for(var i in obj){
        if(obj.hasOwnProperty(i)){
            cloneObj[i]=deepClone(obj[i])
        }
    }
    return cloneObj
}

var test=JSON.parse(JSON.stringify(person))


闭包的实际应用就有防抖和节流
防抖函数:当持续触发事件,一定时间内没有再触发事件 事件处理函数才会执行一次
如果设定的事件到来之前又一次触发了事件就重新开始延时
定时器咯
在设定的时间内 有一次触发了事件 重新开始延时 代表的就是重新开始定时器
那么意味着上一次还没有结束的定时器要清除掉 重新开始延时

var input=document.getElementById('input')
function debounce(delay,value){
    let timer  //内存泄漏
    return function(value){
    clearTimeout(timer)
    timer=setTimeout(function(){
        console.log(value)
    },delay)
    }
}
var debounceFunc=debounce(1000)
input.addEventListener('keyup',function(e){
    debounceFunc(e.target.value)
})
防抖函数的实际应用:
使用echarts时,改变浏览器宽度的时候,希望重新渲染
echarts的图像可以使用此函数提升性能。虽然echarts里有自带的resize函数
还有典型案例就是输入搜索,输入结束后n秒才进行搜索请求,n秒内又输入的内容就重新计时,解决搜索的bug


节流函数:
当持续触发事件的时候 保证一段时间内 只调用一次事件处理函数
一段时间内 只做一件事情

典型案例:鼠标不断点击触发,规定zain秒内多次点击只有一次生效

function thro(func,wait){
    let timeOut
    return function(){
        if(!timeOut){
            timeOut=setTimeout(function(){
                func()
            },wait)
        }
    }
}
doucument.getElementById('button').onclick=thro()


// 图片懒加载里面 防抖节流的应用

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>saa</title>
        <style>
            img{
                display:block;
                max-height:300px;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <h1>aaa</h1>
            <img src="1.png" data-src="1.jpg" alt=""></img>
        </div>
    </body>
        <script>
            var scrollTop=window.scrollY;
            var imgs=Array.from(document.querySelectorAll('img'));
            lazyLoad();
            window.onscroll=()=>{
                scrollTop=window.scrollY;
                lazyLoad();
            }
            function lazyLoad(){

            }
        </script>
</html>
??/?待定哈

js的作用域:
全局作用域:
1.全局作用域在页面打开时被创建,页面关闭时被销毁
2.编写在script标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到
3.在全局作用域中有全局对象window代表一个浏览器窗口,由浏览器创建,可以直接调用
4.全局作用域中声明的变量和函数会作为window对象的属性和方法保存
函数作用域
1.调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁
2.每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
3.在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域内的变量
4.在函数作用域中访问变量,函数时,会先在自身作用域中寻找,若没有找到,则会到函数的上一级作用域中寻找,一直到全局作用域

执行期的上下文
当函数代码执行的前期 会创建一个执行期上下文的内部对象AO(作用域)
这个内部的对象是预编译的时候创建出来的 因为当函数被调用的时候 会先进行预编译
在全局代码执行的前期会创建一个执行器的上下文的对象GO

深层次理解::::
函数作用域预编译
1.创建ao对象 AO{}
2.找形参和变量声明 将变量和形参名当做ao对象的属性名 值为undefined
3.实参和形参相统一
4.在函数体里面找函数声明 值赋予函数体
(函数声明不是函数表达式 var a=function(){}不叫函数声明 叫做函数表达式
  function c(){}才是函数声明

全局作用域的预编译
1.创建go对象
2.找变量声明 将变量名作为go对象的属性名 值为undefined
3.找函数声明 值赋予函数体

if(false){
    var a;
    // a是被声明了的 预编译了 不管条件有没有进来 但还是分配了
}


作用域链:(ao和go的集合
会被保存到一个隐式的属性中去 [[scope]] 这个属性是我们用户访问不到的 但是的确是存在的 让js引擎来访问的 里面存储的是作用域链

js的单例模式
1.只有一个实例 2.可以全局的访问
主要解决:一个全局使用的类 频繁的创建和销毁
何时使用:当你想控制实例的数目 节省系统化资源的时候
如何实现:判断系统是否已经有这个实例 如果有则返回 没有则创建
单例模式的有点:内存中只要一个实例 减少了内存的开销 尤其是频繁的创建和销毁实例( 
比如说是首页页面的缓存
使用场景:1.全局的缓存 2.弹窗

// 1.实现一个登陆的弹框
let loginLayer = (function(){
    let div=document.create('div')
    div.innerHTML('aa')
    div.style.display='none'
    document.body.appendChild(div)
    return div
})()
document.getElementById('button').onclick=function(){
    loginLayer.style.display='block'
}
1.上面这种的 缺点:资源浪费
因为加载完成的时候已经创建好了这个弹窗了 一开始是隐藏状态 弹窗出现
万一人家根本不需要这个弹窗 所以资源浪费
也可以
2.点击的时候创建 (频繁的创建和销毁  在点击的时候创建div 并显示为block呗

3.单例模式呗
// 实现登录
var createLogin=function(){
    var div=document.createElement('div');
    div.innerHTML='aaa'
    div.style.display='none'
    document.body.appendChild(div)
    return div
}
var getSingle=function(fn){
    var result;
    // result是一个div 同一个!
    return function(){
        return result||(result=fn.apply(this,arguments))
    }
}
var create=getSingle(createLogin)
document.getElementById('loginBtn').onclick=function(){
    var loginLay=create(1,2,3)
    loginLay.style.display='block'
}
// es6实现单例模式
class类可以看做是es5构造函数的语法糖

// es5语法:
function Person(name,sex){
    this.name=name
    this.sex=sex
}
Person.prototype.say=function(){
    console.log('');
}
let person1=new Person('a','a')
person1.say()

// es6的实现:
class Person{
    constructor(name,sex){
        this.name=name
        this.sex=sex
        // new 的时候自动调用这个方法 这个方法是做初始化的操作
    }
    say(){
        //...其实是定义在了原型上的方法
    }
}
let p1=new Person('a','a')
p1.say===Person.prototype.say  (true)

// 单例模式如下:
类内的静态方法:static 类自己的方法 可以不需要实例去调用

单例也就是单个实例 虽然是有一个类但我们只需要一个实例去使用 因为这些实例都是一毛一样的为什么不用一个实例去表示
es6没有用到闭包 es5是用到了的
// 创建对象时不要用new 而是用getinstance
class L{
    constructor(name,e,d){
        this.name=name
    }
    static getInstance(name,e,d){
        if(!this.instance){
            this.instance=new L(name,a,a);
            //变量如果没有构造函数定义也可以存在提升情况呗 而且自己方法里面可以new自己呗 牛逼
        }
        return this.instance
    }
}
let a=L.getInstance('a','a');



// 

js是单线程也就是同一时间做同一件事情

arguments对象 参数对象呗
箭头函数没有arguments对象

类数组对象转化为数组用: 
Array.prototype.slice.call(arguments)

哪些操作可能造成内存泄漏:
闭包
意外的全局变量
被遗忘的定时器
脱离dom的引用

高阶函数:将函数作为参数或者返回值的函数

函数是一等公民

// 手写map

var arr=[1,2,3];
var array=arr.map((item,index)=>{
    return item*2;
})
function map(arr,mapCallback){
    if(!Array.isArray(arr)||!arr.length||typeof mapCallback 
    !=='function'){
        return [];
    }
    else{
        let result=[];
        for(let i=0,len=arr.length;i<len;i++){
            result.push(mapCallback(arr[i],i,arr))
        }
        return result;
    }
   
}
map(arr,(item)=>{
    return item*2;
})

js语言:解释性语言 单线程
event loop必须知道哦

// 策略模式:
定义一系列的算法 把他们封装起来 并且他们之间可以相互转换
将算法的使用和算法的实现分离开来
比如说年终奖金发放按照你的等级发放相应奖金-》策略模式

var strategies={
    's':function(salary){
        return salary*4;
    },
    'a':function(salary){
        return salary*2
    }
}
let getBounds=function(level,salary){
    return strategies[level](salary)
}
getBounds('s',1000);
函数也是对象hhh

当然可以把需求分的很细更代码多 但是上面这个对于前端来说很推荐的一种写法且弹性高

// 表单验证 实现
<body>
    <form action='xxx.com' method='post' id="registerForm">
     请输入用户名<input type="text" name="username"></input>
    <button>提交</button>
    </form>   
</body>;
let registerForm=document.getElementById('registerForm')
registerForm.onsubmit=function(){
    if(registerForm.username.value==''){
        alert('name not empty')
        return false
    }
    // .value.length<6
    if(!/^1[3|5|8]]0-9]{9}$/.test(registerForm.phonenumber.value))
    {
        alert('手机号格式不正确')
        return false
    }
}
运用策略模式:封装
validator.add(value,'isNoneempty','用户名不可以为空')
策略对象 一系列的算法 一系列的业务逻辑
let strategies={
    'xxx':function(a,error){
        // 同上
    }
}
假设我有一个验证类 new Validator()
var validateFun=function(){
    let validator=new Validator()
    // 添加验证规则
    validator.add(registerForm.username,'isNonEmpty','用户名不可为空')
    validator.add()
    // 开启验证
    var errorMsg=validator.start()
    return errorMsg
}
registerForm.onsubmit=function(){
    let errorMsg=validateFun()
    if(errorMsg){
        alert(errorMsg)
        return false
    }
}
// 封装策略类 构造函数 class
let Validator=function(){
    this.cache=[]
}
Validator.prototype.add=function(dom,rule,errorMsg){
    let ary=rule.split(':')
    this.cache.push(function(){
        let strategy=ary.shift()
        ary.unshift(dom.value)
        ary.push(errorMsg)
        return strategies[strategy].apply(dom,ary)
        // return strategies[strategy](...ary)
    })
}
Validator.prototype.start=function(){
    for(let i=0,varFunc;varFunc=this.cache[i++];){
        let mas=varFunc()
        if(msg){
            return msg
        }
    }
}
//
function get(a,b,c){
}
let arr=[1,2,3]
// get(arr)  (x)
get.apply(null,arr)  (√)

// 发布订阅模式
1.首先要想好谁是发布者
2.然后给发布者添加一个缓存列表,用于存放回调函数来通知订阅者
3.最后就是发布信息,发布者遍历这个缓存列表,一次触发里面存放的订阅者回调函数
let shopObj={}
shopObj.list=[]
shopObj.listen=function(fn){
    shopObj.list.push(fn)
}
shopObj.trigger=function(){
    for(let i=0,fn;fn=this.list(i++);){
        fn.apply(this,arguments)
    }
}

let event={
    list:[],
    listen:function(key,fn){
        if(!list[key]){
            list[key]=[]
        }
        list[key].push(fn)
    },
    trigger:function(){
        let key=Array.prototype.shift.call(arguments)
        let fns=list[key]
        if(!fns||fns.length==0){
            return
        }
        for(let i=0,fn;fn=fns[i++];){
            fn(...arguments)
        }
    }
}

event.remove=function(key,fn){
    var fns=list[key]
    if(!fns){
        return false
    }
    if(!fn){
        fn&&(fns.length=0)
    }else{
        for(let i=fns.length-1;i>=0;i--){
            let _fn=fns[i]
            if(_fn==fn){
                fns.splice(i,1)
            }
        }
    }
}
一般情况下普通代码高耦合
我们发布订阅模式实现低耦合
你的代码修改了会影响到我的代码:高耦合

$.ajax('http://x.com?login',function(data){
    login.trigger('loginSucc',data);
})
let header=(function(){
    login.listen('loginSucc',function(data){
        header.setAvatar(data.avatar);
    });
    return{
        setAvatar:function(data){
            console.log('')
        }
    }
})();


??
程序员的误区:
过分的在意新技术
{
    满足需求就可 解决问题的能力
    培养解决问题的能力
}
都会一点 但是没有精通的
{
    几年时间足够让你精通某一方面 底层什么的是熟知于心
}
技术上 过多强调硬实力 而忽略了软实力
{
    只能自己工作 是不行的 
    费好大劲做出来的东西自己表达不出来
    和人的交流也要 表达很重要
    心急吃不了热豆腐
}
npm run server

./是当前路径下

数组的扁平化处理
就是将多维数组转化为一维数组
1.
arr.flat(Infinity);
Infinity是正无穷的意思 不管你多少层都包成一层
arr.flat(3) 代表数组一共有三层 则也可以

2.const res=JSON.stringify(arr).replace(/\[|\]/g,'').split(',')
不好的就是数据类型变成了字符串

3.JSON.parse('['+JSON.stringify(arr).replace(/\[|\]/g,'')+']')

4.递归呗
const fn=arr=>{
    for(let i=0;i<arr.length;i++){
        if(Array.isArray(arr[i])){
            fn(arr[i]);
        }else{
            res.push(arr[i])
        }
    }
}
4.reduce
const f=arr=>{
    return arr.reduce(
        (pre,cur)=>{
            return pre.concat(arr.isArray(cur)?f(cur):cur)
        },[]
    )
}
[1,2,[1,2,[6,5],3]]
二维数组时x y二维棋盘格式 但多维就不一定了 x y z多维4d自己脑补不出了

如何创建bfc
父元素里面都是浮动元素 但是父元素撑不起来 他的高度时0 是因为父元素不是bfc盒子

1.float的值不是none
2.position的值不是static或者relative
3.display的值时inline-block,flex或者inline-flex
4.overflow为hidden
bfc可以解决
{
    margin塌陷问题
    阻止浮动元素覆盖  设置子元素bfc
       (但是文本信息不会被浮动元素所覆盖
    咱浮动布局的时候要浮动就都浮动 不浮动就都不浮动
}

发布订阅模式复盘:
var shopObj={}
定义发布者
shopObj.list=[]
缓存列表 存放订阅者订阅的函数

shopObj.listen=function(fn){
    shopObj.list.push(fn)
} 增加订阅者(函数
监听哦 添加监听者(可多个哦

发布信息
shopObj.trigger=function(){
    for(let i=0,fn;fn=list[i++];){
        fn.apply(this,arguments)
    }
}
shopObj.listen(function(color,size){
    console.log(`${color}`);

})

订阅者1
shopObj.trigger('red',42)

这样无法唯一订阅
所以要有key唯一标识
shopObj.trigger('red',42);
shopObj.listen('red',function(size){

})
shopObj.listen=function(key,fn){
    if(!list[key]){
       list[key]=[]
    }
    list[key].push(fn)
}
shopObj.trigger=function(){
    var key=Array.prototype.shift.call(arguments)
    var fns=list[key]
    if(!fns||fns.length==0)return
    for(let i=0,fn;fn=fns[i++];){
        fn.apply(this,arguments)
        可以把一个数组传参且对方函数参数列表是单个的并不是数组哦 apply嘛
        fn(...arguments);
        // 都可以
    }

}
event.remove=function(key,fn){
    let fns=this.list[key]
    if(!fns){
        return false
    }
    if(!fn){
        fn&&(fns.length=0)
    }
    else{
        for(let i=fns.length-1;i>=0;i--){
            let _fn=fns[i]
            if(_fn==fn){
                两个函数比较是可以比较出是否相同的
                fns.splice(i,1)
            }
        }
    }

}

return {
    x:function(){
        他是返回一个对象 而不是返回这个函数 
        返回了一个对象.x可以调用此函数
    }
}
vue之间用发布订阅模式进行组件通信
father.vue

<template>
    <div>
        <input type="text" v-model='name'/>        
        <button @click="doPub">发布</button>
        <Son />
    </div>
</template>
<script>
    import Son from ''
    import pubsub from 'pubsub.js'
    export default{
        name:'father',
        data(){
            return {
                name:''
            }
        },
        props:{
            msg:String
        },
        components:{
            Son
        },
        methods:{
            doPub(){
                pubsub.trigger('item',this.name)
                {/* item是标识 */}
            }
        }
    }
</script>

Son.vue
// 孙子组件也可以订阅爷爷组件的发布
<template>
    <div>
        <h1>{{h1}}</h1>
    </div>
</template>
<script>
import pubsub from  ''
export default{
      
        data(){
            return {
                h1:''
            }
        },
        mounted(){
            let that=this
            {/* 有点类似闭包啦 */}
            pubsub.listen('item',function(data){
                that.h1=data
            })
        }

    }
</script>


把上面封装好的event拿过来放到pubsub.js中
{
    var Event=(function (){
        // 里面不可以是this.list 因为下面有声明list变量list是自己函数内的变量
        var list={},
        listen,trigger,remove;
        listen=function(key,fn){

        }
        trigger=function(){

        }
        remove=function(key,fn){

        }
        return{
            listen,
            trigger,remove
        }
    })()
    export default Event
    
}

js数组中的reduce方法
arr.reduce(function(prev,cur,index,arr){
    ...
},init);
不写init也可以啊

arr表示将要原数组
prev表示上一次调用回调时的返回值,或者初始值init
cur表示当前正在处理的数组元素
index表示当前正在处理的数组元素的索引,若提供init值则索引为0
init表示初始值
常用参数只有prev cur
最后return prev呗

var arr=[1,2,3,4]
var sum=arr.reduce((pre,cur)=>{
    return pre+cur
})
第一次执行的时候 如果没有初始值则pre代表第一项 cur代表第二项
之后pre是上一次调用回调时的返回值

pre默认是第一项吧循环从第二个元素开始  如果不写的话没提供初始值 initialValue ,导致reduce方法将数组的第一项作为了初始值,
所以循环第一次是从数组第二项开始的

let name=preson.reduce((pre,cur)=>{
    if(cur in pre){
        pre[cur]++
    }else{
        pre[cur]=1
    }
    return pre
},{})
prev是一个对象 数组不能是名字+value吧 只能是index+value
数组只能是index 来查找
数组元素可以是对象什么的但只可以用数字下标查找

concat连接可以两个数组连成一个 而不是数组2嵌套到数组1中
返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 
arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,
那么添加的是数组中的元素,而不是数组。

arr.concat(4,5);
// arr数组里面添加了 4 5两个元素 参数可以是多个 地位平等都是元素
cancat就是用于连接数组 而不是字符串呗
人家只是用于数组的

数组方法:reduce map isarray filter foreach indexof slice 也行我靠
字符串方法:concat substring slice split

数组(字符串)使用slice方法的目的就是截取目标数组(字符串)hhh都行 绝了

数组的方法字符串不太能用 字符串的方法数组都可以用呗 (自己觉得是这样

es6的模块化陷阱:

export出的变量不能变吧
undefined+1=NaN
那个题真tm绝了
es6作用域问题 var有提升 预编译是js独有特性

p25视频有个面试题 真他妈绝了 可以看看??

bind()设置this之后再调用 this不会是调用时对象 而是之前设置好的this

bind不会立即执行 且this后续不会被覆盖
以往如果this已经指向了再调用this应该会被覆盖

Math.max只能是参数为整数,浮点数,数字字符串 否则如果是空数组 空变量 非数字字符串 返回NAN
'11.2' yes '11..' no '..1' no
有一个字符不符合则一定不是

Math.max.call(null,...arr)
是可以的 叫做展开运算符的拆包

call apply bind的应用:?
1.可以将伪数组转化成数组
let div=document.getElementsByTagName('div')
console.log(div)

let arr=Array.prototype.slice.call(div)
this覆盖???

Array.prototype.push.apply(arr1,arr2)
arr1.concat(arr2)

应用可以判断数据类型
let arr1=[1,2,3]
function isArray(array){
    return Object.prototype.toString.call(array)==='[object Array]'
    '[object Object]'
    '[object Null]'
}

原型链的继承(多个子类实例继承一个原型  且没有实现super功能,无法对父类进行传参呗

function Parent(){
    this.name='aaa'
}
Parent.prototype.getName=function(){
    return this.name
}
function Child(){

}
Child.prototype=new Parent()
Child.prototype.constructor=Child
const child=new Child()

child.getName()
// 多个实例指向一个原型

构造函数继承:
function Parent(name){
    this.name=name
}
Parent.prototype.getName=function(){
    return this.name
}
function Child(){
    Parent.call(this,'aa')
    // 在子类的构造函数中 执行父类的构造函数 并且为其绑定子类的this 相当于子类上有了父类上的一些属性方法 照搬过来了 算继承吧
    // 因为他没有new 父类对象所以不能够继承父类原型上的方法和属性
}
let child1=new Child()
child1.name[0]='a'


组合式继承:
function Parent(name){
    this.name=name
}
Parent.prototype.getName=function(){
    return this.name
}
function Child(){
    Parent.call(this,'aa')
}
Child.prototype=new Parent()
Child.prototype.constructor=Child

每次new实例都要两遍父类 也不太合理

寄生式组合继承:
function Parent(name){
    this.name=name
}
Parent.prototype.getName=function(){
    return this.name
}
function Child(){
    Parent.call(this,'aa')
}
Child.prototype=Parent.prototype
Child.prototype.constructor=Child
相当于child和father地位相同 且child把father的属性全都占为己有 
这不还是一个原型对象么
??缺点是什么

Child.prototype=Object.create(Parent.prototype)
okkkkkkkkkkkkkkk

直接create父亲不就行了??哦没办法传参 懂了


if(arrs.includes(a)){
    ...
}
console.log(`this is a ${name}`);
Array.includes

提前退出 提前返回
用returnconst printAnimalsDetails=({type,name,gender}={})=>{
    // 参数解构赋值~
    if(!type){
        return 'aaa'
    }
    if(!gender){

    }
}

对象的字面量代替switch语句
const fruit={
    red:['apple'],
    yellow:['banna']
}
function f(color){
    return fruit[color] || []
}

map用法
Map 

let obj1={,,}
let obj2={..}
let obj3={
    [obj1]:'1',
    [obj2]:'2'
}
属性名如果不是字符串他会默认 隐式转换
把obj1变成了字符串 而obj1和obj2的tostring的值时一样的 [object Object]
所以obj3只有一个值
[类型 原型构造函数] ?

map

以往
key => value 
都会变成
string => value

es6之后提出map
map中可以用对象来作为类型属性key 也不叫属性名吧就算是key

不只是string类型对应value了呗

const fruit=new Map().set('red',['apple']).set('yellow',['q1'])
set就是新建呗

return fruit.get(color)

Array.some
Array.every

let fruits=[
    {name:'a',color:'red'},
    {name:'b',color:'yellow'},
]

function f(){
    let isAllRed=fruit.every(f=>f.color=='red')
    //所以的水果都是红色 检测
}

瀑布流:
??
第二行盒子会找到第一行高度最小的下面放置
图片加载是在页面加载之后再加载所以 在onload函数中可以获取其高度并进行操作
onload必须写里面 因为他是加载之后运行的函数 如果不在onload函数里写可能会获取不到高度
onload 事件会在页面或图像加载完成后立即发生。 
他是所有都加载好了 才会执行

window.onload=function(){
    // waterFall()
    function waterFall(){
        let pageWidth=getClient().width
        let itemWidth=item[0].offsetWidth
        let column=parseInt(pageWidth/(itemWidth+gap))
        let arr=[]
        for(let i=0;i<DataTransferItemList.length;i++){
            if(i<columns){
                items[i].style.top=0
                item[i].style.left=(itemWidth+gap)*i +'px'
                arr.push(items[i].offsetHeight)
            }else{
                let minHeight=arr[0]
                let index=0
                // 找到最小的一项 且保存index
                for(){

                }
                items[i].style.top=arr[index]+gap+'px'


            }
        }
    }
}
将上一行最小高度填上照片之后再找上一行最小高度 再放上图片
一行一行来
最小列高度
offset是什么??

兼容 每个浏览器可以获取宽高的方式可能不同
window.innerHeight
document.documentElement.clientHeight
document.body.clientHeight
都是一样的 一毛一样

window.onscroll=function(){
    if(getClient().height + getScrollTop())
}
当滑动到下一个视口窗口的时候 在请求照片路径信息
然后新建div进行增加 添加照片信息
一开始只请求过来一个窗口的照片而已hhh
其实一个蛮快的 请求多点也没事 不然占空间啊

vue的computed
vue中的计算属性
设计他们的初衷是用于简单运算的,在模板中放入太多的逻辑会让模板过重且难以维护
对于任何复杂的逻辑你都应该用计算属性
下面里面我们声明了一个计算属性reversedmessage 我们提供的函数将用作
property vm.reverseMessgae的getter函数
你也可以打开浏览器控制台 自己修改例子里面vm.message值

计算属性和方法:
可以通过在表达式中调用方法来达到同样的效果
两种方法的最终结果确实是完全相同的,然而不同的是 计算属性是基于他们的响应式依赖进行缓存的
只在相关响应式依赖发送改变时他才会重新求值,这意味着只要message没有发生改变
多次访问reversemessage计算属性会立即返回之前的计算结果不必再次执行函数


<body>
    <div id='app'>
        <p>{{message}}</p>
        <p>{{reverseMessgae}}</p>
    </div>
     {/* script */}
     var vm=new Vue({
         el:'#app',
         data:{
             message:'hello'
         },
         computed:{
             reverseMessgae:function(){
                 return this.message.split("").reverse()
                 .join('')
             }
             reverseMessgae:{
                 get:function(){
                     return;
                     {/* 上面的是这个函数的一个简写 */}
                 },
                 set:function(val){
                     可以获取到值
                     {/* 
                     var red=val.split("")
                     this.name1=red[0] 
                     .....
                     */}

                 }
             }
         },
         methods:{
             getMessgae(){
                 return this.message.split('').reverseMessgae().join('')
             }
         }
     })
</body>

数组才有reverse方法


监听器和计算属性的比较:
也叫侦听器哈
vue的computed选项主要用于同步对数据的处理 而watch选项主要用于时间的派发,可异步
这两者可以达到相同的效果 

但是基于他们各自的特点 使用场景会有一些区分
computed拥有缓存属性,只有当依赖的数据发生变化时关联的数据才会变化
适用于计算或者格式化数据的场景 是同步处理数据
watch监听数据,有关联但是没有依赖,只要某个数据发生变化就可以处理一些数据或者派发事件并
同步或异步执行  有关系(可以感受到数据变化)但是自己内部数据并不依赖响应变化
可以异步

两者可以达到相同效果哦

vue实现购物车??务必学会 拜托了
认真 负责对自己 的态度!摆正 对得起别人 对得起自己

侦听器:事件和交互有关的场景
属性作为条件 去判断之后 去触发别的事件=》
是有条件下才触发的 不是实时触发(输入了即触发 不是的 是有条件下才触发的

computed里面的函数虽然是函数但其实是数据 受影响数据

export default{
    name:'test',
    data:{
        Amount:100,
        // 双向绑定
    },
    watch:{
        Amount:function(newVal,oldVal){
            if(newVal>5000){
                alert('cc最大额度可以是5000')
                this.Amount=5000;
            }
        }
    }
}

el:'#app',
data:{

}

<body>
    <div id="app">
        <p>
            <input type="text" v-model="question"></input>
        </p>
    </div>
</body>
var vm=new Vue({
    el:'#app',
    data:{
        question:'',
        answer:''
    }
})

lodash是一个库