3月14日前端学习

123 阅读6分钟

学习新知识确实累,起码比写代码累一些,还要温故而知新,不知道我定不定得住

js事件流之事件捕获和事件冒泡

事件捕获

事件捕获的思想就是从不太具体的dom结点先开始接收事件,而最具体的dom结点最后接收事件,也就是事件从父元素到子元素传播。从外到内,实现事件捕获

事件冒泡

事件冒泡的思想是从最具体的dom结点开始接收事件,逐级向上传播

阻止冒泡

e.stopPropagation

示例代码:

const btn = document.getELmentById('btn');
btn.onclick = function(e){
    e.stopPrapagation()
}
事件委托
  • 事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理
new

通过new创建的对象,可以访问到构造函数的原型对象,通过new操作符,实例对象与构造函数通过原型链连接起来。

构造函数返回值问题
  • 如果构造函数中,用return返回一个原始类型的数据,那么这个数据毫无意义

    function Test(name) {
      this.name = name
      return 1
    }
    const t = new Test('zdk')
    console.log(t.name) // 'zdk' 接收不到return返回的1
  • 如果返回的是对象,return正常工作

手动实现new操作符
function create(Con, ...args) {
  let obj = {}
  Object.setPrototypeOf(obj, Con.prototype)
  let result = Con.apply(obj, args)
  return result instanceof Object ? result : obj
}
​
new一个对象的过程

在内存中创建一个新对象。

  • 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。
  • 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
  • 执行构造函数内部的代码(给新对象添加属性)。
  • 如果构造函数返回对象,则返回该对象;否则,返回刚创建的新对象(空对象)。
有关var,let和const
var的缺陷:
  1. 全局变量挂载到全局对象,全局对象成员污染问题(gobal,windows)

  2. 允许重复的变量声明,导致数据被覆盖

    var a = 1var a = 2
    ​
    console.log(a) // a=2
    
  3. 变量提升:怪异的数据访问,没有块级作用域,只能用闭包来解决

let:
  • let声明的变量不会挂载到全局对象

  • let在同一个作用域不允许重复声明变量

  • let不会有变量提升,不能在声明变量之前使用变量

    实际上let声明的变量在底层实现上也会变量提升,但是提升后会将其放到“暂时性死区”
    如果变量位于暂时性死区,则会报错:ReferenceError: Cannot access 'a' before initialization,当代码运行到该变量声明语句时,会将该变量从暂时性死区中移除
    
const:
  • 声明变量,必须在声明时就要赋值

  • let与const的区别就是const声明的变量不能更改

  • 常量不可变:指的是声明常量的内存空间不变,并不能保证内存空间的地址指向的其他的内存空间

    const a = {
        name:'wzq',
        age:10
    }
    a.name = 'wyf'
    
继承模式
  1. 利用原型来实现继承,但是有显著的缺点,原型上有多个属性的时候,只想继承一个无法实现。

  2. 借用构造函数,利用call回调来实现继承,实例代码如下:

    function Person(name,age){
        this.name = name
        this.age = age
    }
    function Student(name,age,className,score){
        Person.call(this,name,age)//简化操作 person函数执行就是将name和age数据赋值给this的name和age属性
        this.className = className
        this,score = score
    }
    

    无法实现服用,每次都要执行两次函数,影响性能

  3. 原型式继承

  4. 圣杯模式 Father.prototype.name = 'zhang'; function Father(){

        }
        function F(){
    

    } F.prototype = Father.prototype;

        function Son(){
    ​
        }
    

    Son.prototype = new F(); 解释: 1.先创建一个中间层的构造函数F,将Father的原型对象赋值给F函数,现在F继承了Father的原型对象的属性和方法。 2.Son的原型对象是由F构造函数new出来的对象。F函数new的对象继承了Father的原型对象的属性和方法。 3.Son的原型对象拥有了Father原型对象的属性和方法。当修改或添加Son原型对象的属性和方法的时候,修改的是 F函数new的对象,该对象是一个独立的对象(修改的时候Father原型对象不会受到影响),修改的时候Father原型对象不会受到影响 抽象成一个函数,封装功能。 function inherit(Target,Origin){ function F(){}; F.prototype = Orgin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target;//没有这一步的话,son的construcotr会指向Father 这一步可以将son的原型对象归位 }

CSS重磅难点:水平居中与垂直居中
内联元素居中布局
水平布局
  • 行内元素可设置为:text-align:center;
  • flex布局设置父元素:display:flex;justify-content:center;
垂直布局
  • 单行文本父元素确定高度:height
  • 多行文本父元素确定高度:display:table-cell; vertical-align:middle;

块级元素集中布局

  • 水平居中

    1. 定宽:margin:0 auto;
    2. 不定宽:
  • 垂直居中

    1. position:absolute; 调整top,left,margin-left,margin-top(定高)
    2. position:fixed; 设置margin:auto;(定高)
    3. display:table-cell vertical-align:middle
    4. transform:translate(x,y)
    5. flex(不定宽,不定高)
    6. grid(不定宽,不定高)
两栏布局
  1. 通过浮动来实现两栏布局
        .box{
            overflow: hidden;
        }
        .left{
            width: 200px;
            height: 400px;
            background-color: red;
            float: left;
        }
        .right{
            margin-left: 210px;
            height: 200px;
            background-color: green;
        }
        
     <div class="box">
        <div class="left"></div>
        <div class="right"></div>
    </div>

2.通过flex来实现两栏布局

        .box{
            display: flex;
        }
        .left{
            width: 200px;
            height: 400px;
            background-color: red;
        }
        .right{
            height: 200px;
            background-color: green;
            flex: 1;
        }
        <div class="box">
        <div class="left"></div>
        <div class="right"></div>
    </div>

3.通过grid来实现

juejin.cn/post/704743…

圣杯布局

一行三个元素,两边的宽度固定中间的宽度自适应

用flex实现

.box{
            display: flex;
            justify-content: space-between;
        }
        .left{
            width: 200px;
            height: 400px;
            background-color: red;
        }
        .middle{
            flex: 1;
            height: 400px;
            background-color: green;
        }
        .right{
            width: 200px;
            height: 400px;
            background-color: blue;
        }
        <div class="box">
        <div class="left">左边</div>
        <div class="middle">内容</div>
        <div class="right">右边</div>
    </div>
数组去重
1.双循环去重法

先定义一个包含原始数组第一个元素的数组,然后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不重复则添加到新数组中,最后返回新数组

2.set与去重法
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    return [...new Set(arr)]//用解构赋值将set转换成数组
    return Array.from(new Set(arr))//用Array.from方法把set对象变成数组
}
​
3.利用对象属性进行去重
        const arr = [1,2,2,3,4,4,5]
        let obj = {}
        let res = []
        for(let i = 0 ;i<arr.length;i++){
            if(!obj[arr[i]]){
                res.push(arr[i])
                obj[arr[i]] = true
            }
            else{
            }
        }
  1. indexof方法去重
const arr = [1,2,2,3,4,4,5]
        let obj = {}
        let res = []
        for(let i = 0 ; i<arr.length ; i++){
            if(res.indexOf(arr[i])==-1){
                res.push(arr[i])
            }
        }

5.相邻元素去重

let arr = [1,2,2,3,4,4,5]
        let res = []
        arr = arr.sort()
        for(let i = 0; i<arr.length ; i++){
            if(arr[i]!==arr[i-1]){
                res.push(arr[i])
            }
        }

js数组不会报越界错误,可能是因为js数组的底层是hash表