js部分

JS 部分

# 0.JS数据类型

  • 基本数据类型:Number、String、Undefind、null、Bollean、symbol、bigInt
  • 引用数据类型:Object 。包含Object、Array、Fuction
  • 基本数据类型存在栈中,引用数据类型实际的值存在堆中,栈里保存的是引用地址

# 1.闭包

  • 闭包就是能够读取其他函数内部变量的函数,常见表现形式往往是函数返回一个函数。其实现象就是把一个函数的状态和里面的变量能保存下来
  • 原因:只要将闭包赋给一个变量,闭包不会被垃圾回收机制回收。除非这个变量被销毁
//外面的函数,return出内部的函数
function f1() {
  var n = 999

  function f2() {
    alert(n)
  }

  return f2
}

var result = f1()

result() // 999
  • 闭包的用途:

    1. 在函数外部使用函数内部的变量
    2. 隐藏变量
    3. 最主要的用途就是保存外层函数里的变量
  • 闭包的注意点:

    1. 内存消耗很大,过多使用会造成网页的性能问题,在 IE 中可能导致内存泄露
  • 实际使用场景

    1. 防抖节流 因为防抖是在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。每次触发都要看计时器是否存在,所以是需要保存这个计时器的。由于防抖可能会在多处被使用,不适合每次都新建一个全局变量,所以可以利用闭包
    2. 高阶函数、回调函数也属于闭包
    3. 单例模式,会利用闭包。就是用闭包保存一个实例,然后判断如果这个实例不存在则新建一个,如果存在就返回这个实例
    4. 给计算属性传参
    /*
     * fn [function] 需要防抖的函数
     * delay [number] 毫秒,防抖期限值
     */
    function debounce(fn, delay) {
      let timer = null
      //借助闭包
      return function () {
        if (timer) {
          //利用闭包,这个timer在执行完不会被销毁,
          clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
          timer = setTimeOut(fn, delay)
        } else {
          timer = setTimeOut(fn, delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
        }
      }
    }
    let a = debounce(func, 5000) //事件保存到一个变量里,a其实是一个闭包方法。比如click的时候可以直接调用a(func)。重复点击触发a,每次的timer都是同一个
    a(func) //这里就是第一此调用,会在5秒后执行
    a(func) //第二次调用,重置时间
    let b = debounce(func2, 6000) //这是另一处调用防抖的时间,里面的timer和a的不是同一个
    b(func2) //跟a没有任何关联
    
      //单例模式
      let GlobalUser = (function() {
        let instance  // 闭包保存的唯一实例对象
        return function(name) {
          if (instance) return instance
          // (首次)创建实例
          instance = { name: '张三', id: 1003 }
          return instance
        }
    
      })()  // 立即执行,这里相当于把闭包赋给了GlobalUser
      console.log(new GlobalUser('张四').name)   // 张三
      console.log(new GlobalUser('李四').name)   // 张三,依然是张三,复用了第一次创建的实例
      console.log(new GlobalUser() === new GlobalUser())  // true
    
    
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

# 2.深拷贝和浅拷贝的实现方式分别有哪些?

引用类型的数据,在直接赋值的时候,实际上传递的是栈里面的指针。堆里的内容其实是一样的。所以传递过程中不同的变量,实际上对应的是同一个内容。如果一个改变,就会全部改变。

无论是深拷贝还是浅拷贝,其实上要做的是,重新创建一个栈内存地址出来。

浅拷贝的话,是通过某些方法,重新返回出一个新的对象,但是如果对象里面还有对象,里面的对象是不会有新的地址的,所以里面和原对象还存在联系。

深拷贝的话,不管里面有多少层,都会重新创建一个和原对象没有任何联系的新对象

浅拷贝:(1) Object.assign的方式 (2) 通过对象扩展运算符 (3) 通过数组的slice方法 (4) 通过数组的concat方法。
深拷贝:(1) 通过JSON.stringify来序列化对象(如果obj里有函数,undefined,则序列化的结果会把函数或 undefined、原型属性丢失;) 

(2) 手动实现递归的方式。

# 3. call 、bind 、 apply

  • 作用都是改变函数的 this 指向
  • 第一个参数都是 this
  • call 的参数是从第二个参数开始一个个以逗号分隔
  • apply 是第二个传参,以数组形式传进去
  • call 和 apply 作用一样,只是参数形式不一样,当参数个数不定的时候,apply 更好一点
  • bind 和 call 差不多,但是返回值是一个函数,所有调用的时候要最后加个括号 obj.myFun.bind(db)()

# 4.this 指向

https://blog.51cto.com/11871779/2129522
https://zhuanlan.zhihu.com/p/82504422
https://zhuanlan.zhihu.com/p/93446296

# 5.数组操作

# 6.判断一个对象是否是数组

语法: A instanceof B,意思是对象A的原型是否是B.prototype。如果是,返回true,如果不是,返回false

# 7.时间队列顺序

macrotask: script(你的全部JS代码,“同步代码”.有人人为主代码是宏任务第一队列,也有人人为不算宏任务), setTimeout, setInterval, setImmediate, I/O,UI rendering
microtask:process.nextTick,Promises(这里指浏览器原生实现的 Promise), Object.observe, MutationObserver
JS引擎首先执行JS主代码,将microtask queue中的所有任务取出,按顺序全部执行;
然后再从macrotask queue(宏任务队列)中取下一个,执行完毕后,再次将microtask queue(微任务队列)中的时间按照先入先出的顺序全部取出;
循环往复,直到两个queue中的任务都取完。

# 8.数组去重

1.最简单
[...new Set(arr)] //去除不了空对象
3.利用indexOf去重  新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当
前元素,如果有相同的值则跳过,不相同则push进数组
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}
4.利用filter
function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}
5.数组对象去重
unique(arr) {
  const map = new Map();
  //新建一个map,然后开始筛选,判断res里有没有当前item的id,如果有,就返回且把ID放进map,如果没有就过
  return arr.filter((arr) => !map.has(arr.id) && map.set(arr.id, 1))
}

# 9. promise

# 10.JS 事件顺序

参考资料:https://blog.csdn.net/yun_hou/article/details/88697954 (opens new window) 2 https://www.jianshu.com/p/b4fd76c61dc9 (opens new window)

async function async1() {
  console.log('async1 start')
  await async2()
  console.log('asnyc1 end')
}
async function async2() {
  console.log('async2')
}
console.log('script start')
setTimeout(() => {
  console.log('setTimeOut')
}, 0)
async1()
new Promise(function (reslove) {
  console.log('promise1')
  reslove()
}).then(function () {
  console.log('promise2')
})
console.log('script end')

结果

script start
async1 start
async2
promise1
script end
asnyc1 end
promise2
setTimeOut

流程:

  1. 执行 console.log('script start'),输出 script start;
  2. 执行 setTimeout,是一个异步动作,放入宏任务异步队列中;
  3. 执行 async1(),输出 async1 start,继续向下执行;
  4. 执行 async2(),输出 async2,并返回了一个 promise 对象,await 让出了线程,把返回的 promise 加入了微任务异步队列,async1()下面的代码也要等待上面完成后继续执行;
  5. 执行 new Promise,输出 promise1,然后将 resolve 放入微任务异步队列;
  6. 执行 console.log('script end'),输出 script end;
  7. 到此同步的代码就都执行完成了,然后去微任务异步队列里去获取任务
  8. 接下来执行 resolve(async2 返回的 promise 返回的),输出了 async1 end。
  9. 然后执行 resolve(new Promise 的),输出了 promise2。
  10. 最后执行 setTimeout,输出了 settimeout。

# 11. 判断类型

  1. Object.prototype.toString.call() 适用任何类型

    Object.prototype.toString.call(an) // "[object Array]"
    Object.prototype.toString.call('An') // "[object String]"
    Object.prototype.toString.call(1) // "[object Number]"
    Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
    Object.prototype.toString.call(null) // "[object Null]"
    Object.prototype.toString.call(undefined) // "[object Undefined]"
    Object.prototype.toString.call(function () {}) // "[object Function]"
    Object.prototype.toString.call({ name: 'An' }) // "[object Object]"
    
  2. instanceof 只能判断对象类型。就是去原型链里找对应的原型

    [] instanceof Array // true
    <!-- instanceof相当于  array.__proto__ ===  Array.proto-->
    
  3. Array.isArray() 检测是否为数组

# 12. require 和 import 区别

  1. 模块加载的时间 require:运行时加载 import:编译时加载(效率更高)【由于是编译时加载,所以 import 命令会提升到整个模块的头部】

  2. require:模块就是对象,引入时直接引入整个对象

    import:ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入。导入时可以按需导入

# 13. 总结下 var、let 和 const 的区别

  1. var:只有全局作用域和函数作用域概念,没有块级作用域的概念。但是会把{}内也假称为块作用域。

    let:只有块级作用域的概念 ,由 { } 包括起来,if 语句和 for 语句里面的{ }也属于块级作用域。

  2. let 和 var 的第二点不同是,在变量声明之前就访问变量的话,会直接提示 ReferenceError,而不像 var 那样使用默认值 undefined:

    var 存在变量提升,而 let,const 其实也有变量提升,但是暂时性死区所以用 let 定义的变量一定要在声明后再使用,否则会报错。

  3. 可以看出,var:变量可以多次声明,而 let 不允许在相同作用域内,重复声明同一个变量。

  4. 使用 var 和 function 声明的全局变量依旧作为全局对象的属性,使用 let, const 命令声明的全局变量不属于全局对象的属性。

    <script>
      var a = 10; console.log(window.a); //10 console.log(this.a) //10 let b =
      20; console.log(window.b); // undefined console.log(this.b) // undefined
    </script>
    

# 14. js 中的事件委托(事件代理)详解

https://www.cnblogs.com/lauzhishuai/p/11263210.html (opens new window)

就是利用事件冒泡,将子元素的事件写到父元素中,再通过 JS 去判断写不同的方法 核心是 click 父元素,e.target 可以得到实际点击的那个元素,然后就可以进行操作 好处:1.如果子元素很多,需要写很多个 onckilck,事件代理的话只需要写一个。减少 dom 操作 2.如果有新增子元素,不需要手动在写一个 onclick,直接添加就能有

# 15. typeof 和 instanceof

typeof 只能判断基础类型,引用类型都是 object。 instanceof 用来判断一个引用数据类型是否属于某构造函数 https://www.jianshu.com/p/4ff2332228be (opens new window)

var a = [34, 4, 3, 54],
  b = 34,
  c = 'adsfas',
  d = function () {
    console.log('我是函数')
  },
  e = true,
  f = null,
  g

console.log(typeof a) //object
console.log(typeof b) //number
console.log(typeof c) //string
console.log(typeof d) //function
console.log(typeof e) //boolean
console.log(typeof f) //object   !!特殊
console.log(typeof g) //undefined
function Person(name,age,sex){
     this.name = name;
     this.age = age;
     this.sex = sex;
 }

 function Student(score){
     this.score = score;
  }
 var per = new Person("小明"20,“男”)var stu = new Student98);
 console.log(per instanceof Person);  // true
 console.log(stu instanceof Student);  // true
 console.log(per instanceof Object);  // true
 console.log(stu instanceof Object);  // true

# 16. 防抖和节流

https://juejin.cn/post/6963969900742180901?utm_source=gold_browser_extension (opens new window)

防抖使用场景:

  • 按钮防止二次点击

  • 搜索框输入查询

  • scroll 滚动触发

  • 浏览器窗口缩放时,resize 事件

  • vuex 加解密里面,防止初次加载的时候多次加解密

节流使用场景:

  • 浏览器窗口缩放时,resize 事件
  • 抢购按钮

# 17.箭头函数和普通函数的区别

https://www.jianshu.com/p/231a6f58e00b (opens new window)

  1. 箭头函数是匿名函数,不能作为构造函数,不能使用 new
  2. 箭头函数不能绑定 arguments,取而代之用 rest 参数...解决
  3. 箭头函数没有原型属性
  4. 箭头函数的 this 永远指向其上下文的 this,没有办改变其指向,即使是用 apply,call,bind

# 18.函数的 arguments

https://blog.csdn.net/weixin_42561383/article/details/90812917 (opens new window)

每个函数(非箭头)在被调用时都会自动取得两个特殊变量:this 和 arguments。 arguments 是一个类数组对象,里面保存着调用函数时传入的实参,第一个参数索引为 0。

;(function (age, name) {
  console.log(arguments) //[23,"XD"]
  console.log(arguments.length) //2
  console.log(arguments[0]) //23
  console.log(arguments[1]) //XD
})(23, 'XD')

修改 arguments 会影响参数。

;(function (age) {
  console.log(arguments[0]) //23
  console.log(age) //23
  arguments[0] = 18
  console.log(age) //18
})(23)

arguments 虽然可以通过下标获取其中的元素,也有 length 属性,但是却不是一个数组,因此不能使用数组的方法。

;(function () {
  console.log(arguments.reverse()) //报错
})(23, 'XD')

arguments 还有一个 callee 属性,这个属性指向 arguments 所在函数本身。

;(function fun() {
  console.log(arguments.callee === fun) //true
})()

用 arguments 对象判断传递给函数的参数个数,即可模拟函数重载: !!!!重要

function fun() {
  if (arguments.length === 1) {
    console.log(arguments[0])
  } else if (arguments.length === 2) {
    console.log(arguments[0] + arguments[1])
  }
}

fun(18) //18
fun(18, 23) //41

可以使用 Array.prototype.slice.apply(arguments)和 Array.prototype.concat.apply([], arguments)将 arguments 转化为真正的数组。

# 19.for in 和 for of

  • for-in :可以遍历数组和对象、字符串。 总是得到得到下标。为了防止遍历到原型链上的属性,需要配合xx.hasOwnProperty(key)进行判断
  • for-of 不能遍历对象。总是得到数组的 value 或数组、字符串的值,另外还可以用于遍历 Map 和 Set。

# 20.原型链

# 一、原型和原型链

# 1.1 原型

  • 每一个函数,都会有一个默认属性 prototype ,这个就是原型对象,在原型对象上,又会有一个constructor,这个属性又会指向函数本身
  • 当通过构造函数实例化一个对象时,会在对象上添加一个属性__proto__.这里面是构造函数的原型对象。里面会有原型对象里的各种属性和方法。可以直接使用
  • 任何非null数据,其实都是由构造函数创建而来。包括srting,number,object等。所以也都会有对应的__proto__,也就是其构造函数的原型对象。
function fun(name) {
  this.name = name
}

fun.prototype // 原型对象

const obj = new fun('moyuanjun') // 函数被作为构造函数进行调用
obj.__proto__ === fun.prototype // true, 实例对象.__proto__ 指向 构造函数.prototype

# 1.2 原型链

  • 每一个函数都会有自己的原型对象,这个原型对象往往是个object,那也会有自己的__proto__,指向Object函数的原型,而Object的原型对象又是null。
  • 原型链的顶端就是null。

# 1.3 作用

  • 当对象没有这个方法和属性时,会往原型链上一层一层的查找,直接使用。实现继承效果

  • 那在原型上加上属性或者方法,可以让每个由这个构造函数创建出来的对象拥有这个属性和方法,相当于公共方法和属性。

# 1.4 「proto」与「[[Prototype]]」

  • 对象在浏览器重直接打印的话,出来的是[[Prototype]]。因为__proto__并非是ECMAScript标准,是浏览器给我们的一个属性,方便我们操作原型对象。实际上两者是一个东西。[[Prototype]]里面是有__proto__

# 1.5 任何 非空数据 , 本质上都是通过对应 构造函数 构建出来的

而一个数据的数据类型,可以通过原型对象去判断

// 数字
const num = 1
// 数字是通过 Number 构建的, 那么其原型对象等于 Number.prototype
num.__proto__ === Number.prototype // true

// 字符串
const str = 'str'
// 字符串是通过 String 构建的, 那么其原型对象等于 String.prototype
str.__proto__ === String.prototype // true

// 布尔类型
const bool = false
// 布尔值是通过 Boolean 构建的, 那么其原型对象等于 Boolean.prototype
bool.__proto__ === Boolean.prototype // true

// Symbol
const sym = Symbol('symbol')
// sym 是通过 Symbol 构建的, 那么其原型对象等于 Symbol.prototype
sym.__proto__ === Symbol.prototype // true

// BigInt
const big = BigInt(1)
// big 是通过 BigInt 构建的, 那么其原型对象等于 BigInt.prototype
big.__proto__ === BigInt.prototype // true

// 对象
const obj = { age: 18 }
// 对象是通过 Object 构建的, 那么其原型对象等于 Object.prototype
obj.__proto__ === Object.prototype // true

// 函数
const fun = () => {}
// 函数是通过 Function 构建的, 那么其原型对象等于 Function.prototype
fun.__proto__ === Function.prototype // true

// 数组
const arr = [1, 2, 3]
// 数组是通过 Array 构建的, 那么其原型对象等于 Array.prototype
arr.__proto__ === Array.prototype // true

# 二、new做了哪些事情

  1. 创建一个新的空对象 A
  2. 挂载 原型对象: 对象 A 创建 proto 属性, 并将 构造函数 的 prototype 属性赋值给 proto
  3. 改变 构造函数 this 指向, 指向对象 A,并执行函数
  4. 得到一个新对象
1.var obj={};

2.obj.__proto__=test.prototype;

3.test.call(obj);

4.把obj的地址赋值给等式左边的变量

一般是返回第一步创建的对象 A 但是如果 构造函数 也返回了一个对象 B 则返回对象 B 否则返回对象 A

# 21. js循环中断

  1. for循环 直接break

  2. forEach

    return会结束本次循环,进入下一次循环。无中断效果

    break & continue报错

  3. Array.map 同forEach

  4. for...in break对遍历无影响 return报错

  5. for...of break 跳出循环 return 报错

  6. while return报错 break跳出循环

# 22.dom和bom

  • dom:文档对象模型。也就是页面上的dom树
  • bom:浏览器对象模型。是浏览器提供的一系列API。简单的说就是window对象
css部分

css部分

css 部分

http部分

http部分

http 部分