前端必懂概念,你会了吗?

xjj前端妹_雅
发布于 2022-6-13 15:06
浏览
0收藏

执行栈和执行上下文

1.什么是作用域,什么是作用域链?⭐⭐⭐⭐

规定变量和函数的可使用范围称作作用域
每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链。

2.什么是执行栈,什么是执行上下文?⭐⭐⭐⭐

执行上下文分为:

  • 全局执行上下文
    • 创建一个全局的window对象,并规定this指向window,执行js的时候就压入栈底,关闭浏览器的时候才弹出
  • 函数执行上下文
    • 每次函数调用时,都会新创建一个函数执行上下文
    • 执行上下文分为创建阶段和执行阶段
      • 创建阶段:函数环境会创建变量对象:arguments对象(并赋值)、函数声明(并赋值)、变量声明(不赋值),函数表达式声明(不赋值);会确定this指向;会确定作用域
      • 执行阶段:变量赋值、函数表达式赋值,使变量对象编程活跃对象
  • eval执行上下文

执行栈:

  • 首先栈特点:先进后出
  • 当进入一个执行环境,就会创建出它的执行上下文,然后进行压栈,当程序执行完成时,它的执行上下文就会被销毁,进行弹栈。
  • 栈底永远是全局环境的执行上下文,栈顶永远是正在执行函数的执行上下文
  • 只有浏览器关闭的时候全局执行上下文才会弹出

闭包

1.什么是闭包?闭包的作用?闭包的应用?⭐⭐⭐⭐⭐

函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用。

作用:

  • 保护
    • 避免命名冲突
  • 保存
    • 解决循环绑定引发的索引问题
  • 变量不会销毁
    • 可以使用函数内部的变量,使变量不会被垃圾回收机制回收

应用:

  • 设计模式中的单例模式
  • for循环中的保留i的操作
  • 防抖和节流
  • 函数柯里化

缺点:

  • 会出现内存泄漏的问题

原型和原型链

1.什么是原型?什么是原型链?如何理解⭐⭐⭐⭐⭐

原型: 原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型。每个构造方法都有一个显式原型。

  • __proto__是隐式原型;prototype是显式原型
  • 所有实例的__proto__都指向他们构造函数的prototype
  • 所有的prototype都是对象,自然它的__proto__指向的是Object()的prototype
  • 所有的构造函数的隐式原型指向的都是Function()的显示原型
  • Object的隐式原型是null

原型链: 多个__proto__组成的集合成为原型链(概念类似于作用域链)

  • instanceof 就是判断某对象是否位于某构造方法的原型链上。

继承

1.说一说 JS 中的常用的继承方式有哪些?以及各个继承方式的优缺点。⭐⭐⭐⭐⭐

原型继承、组合继承、寄生组合继承、ES6的extend

原型继承


// ----------------------方法一:原型继承
// 原型继承
// 把父类的实例作为子类的原型
// 缺点:子类的实例共享了父类构造函数的引用属性   不能传参
var person = {
	friends: ["a", "b", "c", "d"]
}

var p1 = Object.create(person)

p1.friends.push("aaa")//缺点:子类的实例共享了父类构造函数的引用属性

console.log(p1);
console.log(person);//缺点:子类的实例共享了父类构造函数的引用属性
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

组合继承

// ----------------------方法二:组合继承
// 在子函数中运行父函数,但是要利用call把this改变一下,
// 再在子函数的prototype里面new Father() ,使Father的原型中的方法也得到继承,最后改变Son的原型中的constructor
// 缺点:调用了两次父类的构造函数,造成了不必要的消耗,父类方法可以复用
// 优点可传参,不共享父类引用属性
function Father(name) {
	this.name = name
	this.hobby = ["篮球", "足球", "乒乓球"]
}

Father.prototype.getName = function () {
	console.log(this.name);
}

function Son(name, age) {
	Father.call(this, name)
	this.age = age
}

Son.prototype = new Father()
Son.prototype.constructor = Son
var s = new Son("ming", 20)
console.log(s);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

寄生组合继承


// ----------------------方法三:寄生组合继承
    function Father(name) {
      this.name = name
      this.hobby = ["篮球", "足球", "乒乓球"]
    }

    Father.prototype.getName = function () {
      console.log(this.name);
    }

    function Son(name, age) {
      Father.call(this, name)
      this.age = age
    }

    Son.prototype = Object.create(Father.prototype)
    Son.prototype.constructor = Son

    var s2 = new Son("ming", 18)
    console.log(s2);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

extend


// ----------------------方法四:ES6的extend(寄生组合继承的语法糖)
//     子类只要继承父类,可以不写 constructor ,一旦写了,则在 constructor 中的第一句话
// 必须是 super 。
class Son3 extends Father { // Son.prototype.__proto__ = Father.prototype
	constructor(y) {
		super(200)  // super(200) => Father.call(this,200)
		this.y = y
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

内存泄露、垃圾回收机制

1.什么是内存泄漏⭐⭐⭐⭐⭐

内存泄露是指不再用的内存没有被及时释放出来,导致该段内存无法被使用就是内存泄漏。

2.为什么会导致的内存泄漏⭐⭐⭐⭐⭐

内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法释放,积少成多,系统会越来越卡以至于崩溃。

3.垃圾回收机制都有哪些策略?⭐⭐⭐⭐⭐

  • 标记清除法
    • 垃圾回收机制获取根并标记他们,然后访问并标记所有来自它们的引用,然后在访问这些对象并标记它们的引用…如此递进结束后若发现有没有标记的(不可达的)进行删除,进入执行环境的不能进行删除
  • 引用计数法
    • 当声明一个变量并给该变量赋值一个引用类型的值时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代的时候,该计数-1,当计数变为0的时候,说明无法访问该值了,垃圾回收机制清除该对象
    • 缺点:当两个对象循环引用的时候,引用计数无计可施。如果循环引用多次执行的话,会造成崩溃等问题。所以后来被标记清除法取代。

深拷贝和浅拷贝

1.手写浅拷贝深拷贝⭐⭐⭐⭐⭐、


// ----------------------------------------------浅拷贝
// 只是把对象的属性和属性值拷贝到另一个对象中
var obj1 = {
	a: {
		a1: { a2: 1 },
		a10: { a11: 123, a111: { a1111: 123123 } }
	},
	b: 123,
	c: "123"
}
// 方式1
function shallowClone1(o) {
      let obj = {}

      for (let i in o) {
        obj[i] = o[i]
      }
      return obj
}
// 方式2
var shallowObj2 = { ...obj1 }
// 方式3
var shallowObj3 = Object.assign({}, obj1)

let shallowObj = shallowClone1(obj1);

shallowObj.a.a1 = 999
shallowObj.b = true

console.log(obj1);  //第一层的没有被改变,一层以下就被改变了


// ----------------------------------------------深拷贝

// 简易版  
function deepClone(o) {
      let obj = {}
      for (var i in o) {
        // if(o.hasOwnProperty(i)){
        if (typeof o[i] === "object") {
          obj[i] = deepClone(o[i])
        } else {
          obj[i] = o[i]
        }
        // }
      }
      return obj
}

var myObj = {
      a: {
        a1: { a2: 1 },
        a10: { a11: 123, a111: { a1111: 123123 } }
      },
      b: 123,
      c: "123"
}

var deepObj1 = deepClone(myObj)
deepObj1.a.a1 = 999
deepObj1.b = false
console.log(myObj);

// 简易版存在的问题:参数没有做检验,传入的可能是 Array、null、regExp、Date
function deepClone2(o) {
      if (Object.prototype.toString.call(o) === "[object Object]") {  //检测是否为对象
        let obj = {}
        for (var i in o) {
          if (o.hasOwnProperty(i)) {
            if (typeof o[i] === "object") {
              obj[i] = deepClone(o[i])
            } else {
              obj[i] = o[i]
            }
          }
        }
        return obj
      } else {
        return o
      }
}

function isObject(o) {
      return Object.prototype.toString.call(o) === "[object Object]" || Object.prototype.toString.call(o) === "[object Array]"
}
// 继续升级,没有考虑到数组,以及ES6中的map、set、weakset、weakmap
function deepClone3(o) {
      if (isObject(o)) {//检测是否为对象或者数组
        let obj = Array.isArray(o) ? [] : {}
        for (let i in o) {
          if (isObject(o[i])) {
            obj[i] = deepClone(o[i])
          } else {
            obj[i] = o[i]
          }
        }
        return obj
      } else {
        return o
      }
}


// 有可能碰到循环引用问题  var a = {}; a.a = a; clone(a);//会造成一个死循环
// 循环检测
// 继续升级
function deepClone4(o, hash = new map()) {
      if (!isObject(o)) return o//检测是否为对象或者数组
      if (hash.has(o)) return hash.get(o)
      let obj = Array.isArray(o) ? [] : {}

      hash.set(o, obj)
      for (let i in o) {
        if (isObject(o[i])) {
          obj[i] = deepClone4(o[i], hash)
        } else {
          obj[i] = o[i]
        }
      }
      return obj
}

// 递归易出现爆栈问题
 //  将递归改为循环,就不会出现爆栈问题了
var a1 = { a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' };
var b1 = { b: { c: { d: 1 } } }
function cloneLoop(x) {
      const root = {};
      // 栈 
      const loopList = [  //->[]->[{parent:{a:1,b:2},key:c,data:{ c1: 3, c2: { c21: 4, c22: 5 } }}]
        {
          parent: root,
          key: undefined,
          data: x,
        }
      ];
      while (loopList.length) {
        // 深度优先
        const node = loopList.pop();
        const parent = node.parent; //{} //{a:1,b:2}
        const key = node.key; //undefined //c
        const data = node.data; //{ a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' }  //{ c1: 3, c2: { c21: 4, c22: 5 } }}
        // 初始化赋值目标,key 为 undefined 则拷贝到父元素,否则拷贝到子元素
        let res = parent; //{}->{a:1,b:2,d:'asd'} //{a:1,b:2}->{}
        if (typeof key !== 'undefined') {
          res = parent[key] = {};
        }
        for (let k in data) {
          if (data.hasOwnProperty(k)) {
            if (typeof data[k] === 'object') {
              // 下一次循环 
              loopList.push({
                parent: res,
                key: k,
                data: data[k],
              })
            } else {
              res[k] = data[k];
            }
          }
        }
      }
      return root
}


function deepClone5(o) {
      let result = {}
      let loopList = [
        {
          parent: result,
          key: undefined,
          data: o
        }
      ]

      while (loopList.length) {
        let node = loopList.pop()
        let { parent, key, data } = node
        let anoPar = parent
        if (typeof key !== 'undefined') {
          anoPar = parent[key] = {}
        }

        for (let i in data) {
          if (typeof data[i] === 'object') {
            loopList.push({
              parent: anoPar,
              key: i,
              data: data[i]
            })
          } else {
            anoPar[i] = data[i]
          }
        }
      }
      return result
}


let cloneA1 = deepClone5(a1)
cloneA1.c.c2.c22 = 5555555
console.log(a1);
console.log(cloneA1);

// ------------------------------------------JSON.stringify()实现深拷贝
function cloneJson(o) {
      return JSON.parse(JSON.stringify(o))
}
// let obj = { a: { c: 1 }, b: {} };
// obj.b = obj;
// console.log(JSON.parse(JSON.stringify(obj))) // 报错 // Converting circular structure to JSON
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
引用本文链接:https://blog.csdn.net/qq_33277654/article/details/122924692
引用作者:晟小明
引用文章来源:CSDN博客

已于2022-6-13 15:40:46修改
3
收藏
回复
举报
3
3
3条回复
按时间正序
/
按时间倒序
xjj_snntH
xjj_snntH

忘了可以回来在看看

1
回复
2022-6-13 15:48:56
xjjFeRookie
xjjFeRookie

可以,打好基础很重要!

1
回复
2022-6-13 15:53:31
xjj_xnkl
xjj_xnkl

大佬出品实用性很强,学到了

1
回复
2022-6-13 16:35:41


回复
    相关推荐
    这个用户很懒,还没有个人简介
    觉得TA不错?点个关注精彩不错过
    帖子
    视频
    声望
    粉丝
    最近发布
    社区精华内容