js面经总结

一、js基础语法

1.1变量类型和计算

1.1.1变量类型

(1)值类型vs引用类型

值类型:

 指向值本身

undefine boolean string number  symbol(表示独一无二的值,最大的用法是用来定义对象的唯一属性名)

引用类型:

变量指向值的存储地址

 数组 对象  null(指向空地址)  函数 

(2) typeof运算符

 作用:识别出所有的值类型、函数、判断是否是引用类型(不可再细分)

类型:undefine/string/boolean/number/symbol/object/function

(3)深拷贝

 浅拷贝:引用类型只是拷贝值地址

               

<script>
    const obj = {
      age:20,
      name:'hhh',
      address:{
        city:'beijing',

      },
      arr:['a','b','c']

    }
    function deepClone(obj={}){
      if(typeof obj != 'object' || obj==null){
        return  obj
      }
      let result
      if (obj instanceof Array){
        result = []
      }else{
        result = {}
      }
      for(key in obj){
        if(obj.hasOwnPropety){
          result[key] = deepClone[obj[key]]

        }
        
      }

    }
  </script>

1.1.2变量计算

      (1)类型转换 

               字符串拼接

               ==  (尽量通过某种方式转换后变成相等)

何时使用===何时使用==

答:除了==null使用==,其他时候都用===

(2) if语句和逻辑运算

falsely变量:

!!0 ===false

!!NAN ===false

!!''===false

!!null === false

!!undefine ===false

!!false ===false

truely变量:

除了false变量之外的都是truely变量

 

if语句判断的就是truely变量和falsely变量

1.2原型和原型链

1.2.1class和继承

class Person{   
      constructor(name,age){
        this.name = name
      }
      eat (){
        console.log (
          `${this.name} eating something`
        )
      }
    }
    class Student extends Person{
      constructor(name,number){
        super(name)
        this.number = number
      }
      sayHi (){
        console.log(`姓名:${this.name} 学号:${this.number}`)
      }
    }
    xialuo = new Student('xialuo','0002')

1.2.2类型判断instanceof

判断对象是否在原型链上,eg p instanceof Object

1.2.3原型和原型链

原型关系

每个class都有显示原型 prottotype,

每个实例对象都有隐式原型__proto__

实例的__proto指向class的prottotype

基于原型的的执行规则

获取属性xialuo.name或者执行方法xialuo.sayHi()时,

先从自身属性和方法查找,

找不到则自动去__proto__找

原型链

xiaoluo.__proto__ === Student.prottotype

Student.prottotype.__proto__ === Person.prottotype

  ps:

class是ES6语法规范,由ECMA委员会发布

ECMA只规定语法规则,即我们代码的书写规范,不规定如何实现

以上实现方式都是V8引擎的实现方式,也是主流

如何判断一个变量是数组(原型链)    答:p instanceof  Array

手写一个jQuery,考虑插件和可扩展性 

考虑插件则都在jquery原型里面添加

复写机制

使用继承

class Jquery{
      constructor(selector){
        const result = document.querySelectorAll(selector)
        const length = result.length
        for(let i = 0 ; i < length ;i++){
          this[i] = result[i]
        }
        this.length = length
        this.selector = selector
      }
      get(index){
        return this[index]
      }
      each(fn){
        for(let i = 0; i < this.length ; i++){
          const elem = this[i]
          fn(elem)
        }
      }
      on(type,fn){
        return this.each(elem =>{
          elem.addEventListener(type,fn,false)
        })
      }
    }

class的本质,怎么理解       答:原型和原型链图示

 

1.3作用域和闭包

作用域和自由变量

作用域

全局

块级(ES6新增)

函数

自由变量

一个变量在当前作用域没有定义但被使用了

向上级一层一层去找,直到找到为止

如果全局作用域都没有找到,则报错XXX is not undefined

闭包

作用域应用的特殊情况,有两种表现: 

ps:所有的自由变量的查找是在函数定义的或者向上一级作用域查找 ,不是在执行的地方查找。。。

1.函数作为参数传递

// 函数作为参数
   function print (fn){
     let a = 200
     fn ()
   }
   let a = 100
   function fn (){
     console.log(a)
   }


   print (fn)   //打印100,因为fn在全局作用域中定义,打印的a在fn中没找到,会向上一级找即全局a

2.函数作为返回值被返回

// 作为函数返回
   function create(){
     let a = 100
     return function (){
       console.log(a)
     }
   }
   let fn = create()
   let a = 200  
   fn()   //打印100  因为fn等于create(),s所以在create定义中找a的值,即100

this

作为普通函数

使用call  bind apply

作为对象方法被调用

在class方法中调用

在箭头函数中调用   --箭头函数的this取值时取上一级作用域的取值

ps:this取什么值是在函数执行时确定的

题目:this的不同应用场景如何取值

答案:

1.被当作普通函数调用

2.使用call\bind\apply

3.作为对象方法被调用

4.在class方法中调用

5.在箭头函数中调用   --箭头函数的this取值时取上一级作用域的取值

题目:手写bind函数

Function.prototype.bind1 = function (){
      //把arguments赋值给Array.prototype.slice中国this
      const args =  Array.prototype.slice.call(arguments)   
      // 弹出第一项作为this
      const t = args.shift()
      // fn1.bind中的fn1,因为谁调用执行就指向谁
      const self = this  
      // 返回一个函数
      return function (){
        return self.apply(t,args)
      }
    }

    function fn1 (a,b,c){
      console.log('this',this)
      console.log(a,b,c)
      return 'this is fn'

    }
    const fn2 = fn1.bind({x:10},10,20,30)
    const res = fn2()
    console.log(res)

题目:闭包的应用

1.隐藏数据

2.做一个简单的cache工具

// 闭包隐藏数据只提供API
    function createCache (){
       const data = {}  //闭包的数据,不被外界访问
       return {
         set:function (key,val){
           data[key] = val

         },
         get:function (key){
           return data[key]

         }
       }

    }
    const c = createCache()
    c.set('a',100)
    c.get(c)

题目:创建10个a,点击弹出序号

let a;
    for(let i = 0; i < 10 ; i++){
      a = document.createElement('a');
      a.innerHTML = i ;
      a.addEventListener('click',function(e){
//1-9,因为在for中定义变成块级作用域
        e.preventDefault();
        alert(i);

      })
      document.body.appendChild(a);
    }


let i,a;
    for(i = 0; i < 10 ; i++){
      a = document.createElement('a');
      a.innerHTML = i ;
      a.addEventListener('click',function(e){
//每次点击i都是,因为在触发点击时,i会在全局作用域中找,全局的i的值会变成10
        e.preventDefault();
        alert(i);

      })
      document.body.appendChild(a);  
    }

1.4异步和单线程

单线程和异步

js是单线程语言,只能同时做一件事

浏览器和nodejs已经支持js启动进程,如web worker

js和DOM渲染共用一个线程,因为js可改变DOM结构

所以:

遇到等待(网络请求和定时任务)不能卡住

异步和同步

js是单线程语言

异步(callback回调函数)不会阻塞代码执行,

同步会阻塞代码执行

应用场景

网络请求,如ajax请求图片加载

定时任务,如setTimeout

callback hell 和promise

promise把callback的形式变成管道形式

题目:同步和异步的区别?

js是单线程语言

异步(callback回调函数)不会阻塞代码执行,

同步会阻塞代码执行

题目:手写promise加载一张图片

 function loadImg(src){
     return new Promise(
       (resolve,reject) => {
          const img = document.createElement('img')
          img.onload = () =>{
            resolve(img)
          }
          img.onerror = () =>{
            reject(new Error('图片加载失败'))
          }
          img.src= src
       }
     )
   }
   const url1 = 'https://img.mukewang.com/5fbb1c720001b5a317920764.jpg'
   const url2 = 'https://img3.mukewang.com/5fb357ce000182d405400200.jpg'
   loadImg(url1).then(img =>{
     //箭头函数前的img接收的是resolve中的img对象
     console.log(img.width)
     return img  //普通对象
   }).then( img =>{
     //箭头函数前的img是上一个then返回的img
     console.log(img.height)
     return loadImg(url2)  //promise实例对象即resolve中的img

   }).then(img2 =>{
    console.log(img2.width)
   }).catch(e => console.log(e))
   

题目:前端异步使用场景

网络请求,如ajax请求图片加载

定时任务,如setTimeout

二、js-web-api

1、js基础和js-web-api的区别

1.js是=基础是规定语法(ECMA262标准)

2.JS web API 是网络操作的API(W3c标准)

3.前者是后者的基础,两者结合才能真正实际应用

2、DOM

2.1DOM的本质

数结构

2.2DOM节点操作

获取dom节点

arribute

DOM节点的属性,

propety

修改对象属性,不会体现在HTML结构中

2.3DOM结构操作

新增/插入节点

获取子元素列表,获取父元素

删除子节点

const div1 = document.getElementById('idv1')
    // 新建节点
    const p1 = document.createElement('p')
    p1.innerHTML = 'this is p'
    // 插入节点
    div1.appendChild(p1)
    // 移动节点
    div2 = document.getElementById('div2')
    const pp = document.getElementById('pp')
    div2.appendChild(pp)
    //获取父元素
    console.log(pp.parentNode)
    //h获取子元素列表
    const list = Array.prototype.slice.call(div1.childNodes).filter(child =>{
      if(child.nodeType===1){
        return true
      }else{
        return false
      }
    })
    console.log(list)
    // 删除子元素
    div1.removeChild(list[0])

2.4DOM性能

dom操作是非常昂贵的,避免频繁的DOM操作

对DOM查询进行缓存

将频繁操作改为一次性操作

题目:DOM是什么数据结构

树(DOM树)

题目:DOM操作的常见API

DOM节点操作:获取dom节点

DOM结构操作:新增/插入节点、获取子元素列表,获取父元素、删除子节点

attr和propety操作

题目:attr和propety的区别

attr:修改HTML属性,会改变HTML结构

propety:修改对象属性,不会体现在HTML结构中

两者都有可能引起DOM重新渲染

尽量使用propety,修改DOM结构比较消耗性能

题目:一次性插入DOM节点考虑性能

dom查询缓存、将频繁操作改为一次性插入

3、BOM(browser Object Model)

navigator

screen

location

history

 // navigator
    const ua = navigator.userAgent
    const isChrome = ua.indexOf('chrome')
    console.log(isChrome)

    // screen
    console.log(screen.width)
    console.log(screen.height)

     

    // location    
    url = 'https://class.imooc.com/class/chapter/115.html?a=100&b=100#author'
    console.log(location.href)   //https://class.imooc.com/class/chapter/115.html?a=100&b=100#author
    console.log(location.protocol)  // http:    https:
    console.log(location.host) //class.imooc.com
    console.log(location.pathname)   //   /class/chapter/115.html
    console.log(location.search)  //a=100&b=100
    console.log(location.hash)  //#author

    // history
    history.back()
    history.forward()

题目:如何识别浏览器类型

题目:分析拆解url

 

4、事件绑定

事件绑定

事件冒泡

事件代理

优点:代码简洁,减少浏览器内存占用,但是不要滥用

 

题目:绑定一个事件通用的事件监听函数

function bindEvent (elem,type,selector,fn){
      if (fn == null){
        fn = selector
        selector = null
      }
      elem.addEventListener (type, function(e) {
        let target
        if (selector) {
          // 需要代理
          target = e.target
          if(target.matches(selector)){
            fn.call(target,e)
          }
          
        }else{
          //不需要代理
         // fn(e)
           fn.call(target,e)
        }

      })
      
    }
    const btn1 = document.getElementById('btn1')
    bindEvent(btn1,'click',function(e){
      e.preventDefault()
      alert(btn1.innerHTML)

    })
    const div1 = document.getElementById('div1')
    bindEvent(div1,'click','a',function(e){
      e.preventDefault()
      alert(this.innerHTML)

    })

题目:描述事件冒泡流程

基于DOM树形结构

事件顺着触发元素往上冒泡

应用场景:d代理

题目:无限下拉的图片列表,如何监听每个图片的点击

使用事件代理

5、ajax

XMLHttpRequest

// const xhr = new XMLHttpRequest()
    // xhr.open('get','api',true)
    // xhr.onreadystatechange = function(){
    //   if(xhr.readyState === 4){
    //     if(xhr.status === 200){
    //       console.log(
    //         JSON.parse(xhr.responseText)
    //       )
         

    //     }else{
    //       console.log('其他情况')
    //     }
    //   }
    // }
    // xhr.send(null)
const xhr = new XMLHttpRequest()
    xhr.open('POST','login',true)
    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
        if(xhr.status === 200){
          console.log(
            JSON.parse(xhr.responseText)
          )
         

        }else{
          console.log('其他情况')
        }
      }
    }
    postData = {
      userName:'zhangsan',
      password:'123456'
    }
    xhr.send(JSON.stringify(postData))

状态码(readyState )

0:未初始化,即还没发送send()方法

1:已载入,已调用send方法,正在发送请求

2.载入完成,seng方法执行完成,已经接受到全部相应内容

3.交互,正在解析相应内容

4.完成,相应内容解析完成,可以在客户端调用

xhr.status 

2XX:表示请求成功,如200

3XX,需要重定向,浏览器直接跳转,如301 302  304

4XX:客户端错误  如404(页面找不到)  403(无登录时五权限)

5XX:服务端错误

跨域,同源策略,跨域解决方案

是什么同源策略

axjax请求时,浏览器要求当前网页和server必须同源(安全)

协议、域名、端口必须一致就是同源

什么是跨域:

图片 /css/js都可以跨域即  link/img/script标签可以跨域

<img/>可以=用与统计打点,可使用第三方统计服务

<link><script>可以使用CDN,CDN一般都是外域

<script>可以实现jsonp

跨域:

所有的跨域都是经过server端允许和配合的

未经过guoserver端实现跨域,说明浏览器有漏洞,威胁信号

jsonp

<script>标签可以实现跨域

服务端可以任意动态的拼接数据返回

所以<script>就可以获取跨域的数据,只要服务端愿意返回

 <script>

        var script=document.createElement("script")
            script.src = "url+ '&callback=' + funcName"
        document.body.appendChild(script)

        window[funcName] = function (data) {

        callback(data)

      }

    </script>

CORS

服务端操作

题目:

1.手写一个简易ajax

function ajax(url){
      const p = new Promise((resolve,reject) =>{
        const xhr = new XMLHttpRequest()
        xhr.open('get','data.json',true)
        xhr.onreadystatechange = function (){
          if(xhr.readyState === 4){
            if(xhr.status === 200){
              resolve(
                JSON.parse(xhr.responseText)
              )
            }else if(xhr.status === 404){
              reject(
                new Error('404 not found')
              )

            }
          }
        }
        xhr.send(null)

      })
      return p

    }

2.跨域常见解决方法

jsonp\cors

请求发送了解axios/fetch/xmlhttprequest

6、存储

  • cookie

    • 本身用于客户端和服务器端通信

    • 被“借用”到本地存储

    • 容量只能存储4KB

    • 所有http请求都带着

    • 只能通过 document.cookie="a=100;b=200"来修改,太过简陋   同一个key会覆盖,不同key会追加

  • sessionStorage

    sessionStorage.setItem(key,value);sessionStorage.getItem(key);   只存于当前会话,浏览器关闭则清空   

  • localStorage(常用)

    HTML5专门为存储而设计,最大5MB

    api简单易用  ,永久存储,除非代码删除或者手动删除   一般使用localStorage,很少使用sessionStorage

    localStorage.setItem(key,value);localStorage.getItem(key);

    注意:在IOS Safari隐藏模式下localStorage.getItem(key);会报错,建议使用try-catch

    区别

    容量区别

    是否会携带到http请求中(cookie所有都要带)

    api易用性(cookie要封装,其他两个可以直接用)

 

三、开发环境

git常用命令

git add .

git checkout XXX

git commit -m 'xxx'

git push origin master

git pull origin master

git branch 

 

四、运行环境

运行环境即前端是浏览器(统称)

下载代码,渲染出页面,期间会执行js

要保证代码在浏览器:稳定高效

 

1、网页加载过程

1.1页面加载和渲染过程

资源的形式

html代码

媒体文件,如图片视频等

javascript  css

加载形式

DNS解析  :域名 ---IP

浏览器根据IP地址向服务器发起http请求

服务器处理http请求,并返回给浏览器

渲染过程

根据HTML代码生产DOM树

根据CSS代码生成CSSDOM

将DOM树和CSSDOM整合成Render Tree

根据Render Tree渲染页面

遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续   (js和页面渲染公用一个线程,js有可能更改dom树)

直至把Render Tree渲染完成

 

题目:从输入url到渲染页面过程

下载资源:

渲染资源

题目:window.onload和DOMContentLoanded的区别

2、性能优化

性能优化原则:

多使用内存、缓存、或者其他方法

减少CPU计算量,减少网络加载耗时

以上方法就是空间换时间

从何入手

让加载更快

减少资源体积:压缩代码

减少访问次数:合并代码,SSR服务端渲染,缓存

使用更快的网络:CDN

让渲染更快

CSS放在head,js放在body最下面

尽早执行JS,使用DOMContentLoaded触发

懒加载,图片懒加载,上滑加载更多(可以加一张预览图)

对DOM查询进行缓存

频繁DOM操作,合并到一起插入DOM树

节流throttle防抖debounce

 

节流throttle

拖拽一个元素时,要随时拿到推拽元素的位置

直接用drag事件,则会频繁触发,很容易导致卡顿

节流:无论拖拽速度多块,都会每个100ms触发一次

防抖debounce

监听一个输入框,文字变化后触发change事件

直接用keyup则会频繁触发change事件

防抖:当用户输结束或者暂停时,才会触发change事件

function debounce(fn,delay=500){
      let timer = null
      return function (){
        if(timer){
          setTimeout(timer)
        }
        timer = setTimeout(() =>{
          fn.apply(this,arguments)
          timer = null
        },delay)
      }

    }
    const input1 = document.getElementById('input')
    input1.addEventListener('keyup',debounce(() =>{
        console.log(input1.value)
      }
    ),600)

 

 

3、安全

常见的攻击方式

XSS攻击

或者使用xss工具

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值