amenzai / myDiary

Record what you do every day
4 stars 0 forks source link

JS 高级面试 #53

Closed amenzai closed 5 years ago

amenzai commented 6 years ago

JS高级面试

ES6 常用语法

模块化的使用和编译环境

webpack笔记 babel使用 rollup.js 使用:

Class 与 JS 构造函数的区别

参考1-构造函数和集成ES5 参考2-class 和 extends

Promise 的用法

参考

ES6 其他常用功能

参考

原型高级应用

原型如何实际应用

jquery 和 zepto 的简单使用 他俩如何使用原型

zepto 如何使用原型

var zepto = {}
zepto.init = function(selector) {
  // resoure code 略 only write prototype
  var slice = Array.prototype.slice
  var dom = slice.call(document.querySelectorAll(selector))
  return zepto.Z(dom, selector)
}

// use
var $ = function(selector) {
  return zepto.init(selector)
}

// 构造函数
function Z(dom, selector) {
  var i, len = dom ? dom.length : 0
  for(i = 0; i < len; i++) {
    this[i] = dom[i]
  }
  this.length = len
  this.selector = selector || ''
}
zepto.Z = function(dom, selector) {
  return new Z(dom, selector)
}

$.fn = {
  constructor: zepto.Z,
  css: function(key, value) {

  },
  html: function(value) {

  }
}
zepto.Z.prototype = Z.prototype = $.fn

jQuery 如何使用原型

(function(window) {

  var jQuery = function(selector) {
    return new jQuery.fn.init(selector)
  }

  jQuery.fn = {
    constructor: jQuery,
    css: function(key, value) {
      alert('css')
    },
    html: function(value) {
      return 'html'
    }
  }

  var init = jQuery.fn.init = function(selector) {
    var slice = Array.prototype.slice
    var dom = slice.call(document.querySelectorAll(selector))

    var i, len = dom ? dom.length : 0
    for (i = 0; i < len; i++) {
      this[i] = dom[i]
    }
    this.length = len
    this.selector = selector || ''
  }

  init.prototype = jQuery.fn

  window.$ = jQuery

})(window)

原型如何满足扩展

$.fn.myMethod = function() {}

异步全面讲解

什么是单线程,和异步有何关系?什么是 event-loop

单线程:只有一个线程,同时只能做一件事(for 循环多次、alert),两段JS不能同时执行 原因:避免DOM渲染的冲突 解决方案:异步

什么是异步队列?何时被放入异步队列?轮询的过程

目前 JS 解决异步的方案有哪些

如果只用 jQuery 如何解决异步

jquery1.5的变化:

$.ajax().done().fail().done() $.ajax().then(function(){}, function(){}).then()

jquery-deferred: 对扩展开放,对修改关闭 dtdAPI分为两类,用意不同:

function waitHandle() {
    // 定义
    var dtd = $.Deferred()
    var wait = function(dtd) {
        var task = function() {
          console.log('执行完成')
            // 成功
          dtd.resolve()
            // 失败
            // dtd.reject()
        }
        setTimeout(task, 1000)
          // wait 返回
        return dtd.promise()
      }
      // 最终返回
    return wait(dtd)
  }

  // 使用(B 员工)
  var w = waitHandle() // promise 对象
  $.when(w).then(function() {
    console.log('ok 1')
  }, function() {
    console.log('err 1')
  })

Promise 的标准

问题:

async / await 的使用

问题:

虚拟DOM

什么是Vdom,为何要用Vdom

设计一个需求场景 -》用jquery实现 -》遇到的问题

浏览器渲染 DOM 是很慢的,运行 JS 快成一匹马

// 点击按钮,改变表格数据
// jquery demo

// html
// <div id="container"></div>
// <button id="btn-change">change</button>

// js
var data = [{
    name: '张三',
    age: '20',
    address: '北京'
  },
  {
    name: '李四',
    age: '21',
    address: '上海'
  },
  {
    name: '王五',
    age: '22',
    address: '广州'
  }
]

// 渲染函数
function render(data) {
  var $container = $('#container')

  // 清空容器,重要!!!
  $container.html('')

  // 拼接 table
  var $table = $('<table>')

  $table.append($('<tr><td>name</td><td>age</td><td>address</td>/tr>'))
  data.forEach(function (item) {
    $table.append($('<tr><td>' + item.name + '</td><td>' + item.age + '</td><td>' + item.address +
      '</td>/tr>'))
  })

  // 渲染到页面
  $container.append($table)
}

$('#btn-change').click(function () {
  data[1].age = 30
  data[2].address = '深圳'
  // re-render  再次渲染
  render(data)
})

// 页面加载完立刻执行(初次渲染)
render(data)

// 存在的问题就是,没有改变数据的地方,也会重新渲染

遇到的问题:

vdom:

Vdom如何使用,核心函数有哪些

snabbdom用法:

// <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script>
// <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script>
// <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script>
// <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script>
// <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js"></script>
// <script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.js"></script>
var snabbdom = window.snabbdom

// 定义 patch
var patch = snabbdom.init([
  snabbdom_class,
  snabbdom_props,
  snabbdom_style,
  snabbdom_eventlisteners
])

// 定义 h
var h = snabbdom.h

var container = document.getElementById('container')

// 生成 vnode
var vnode = h('ul#list', {}, [
  h('li.item', {}, 'Item 1'),
  h('li.item', {}, 'Item 2')
])
patch(container, vnode)

document.getElementById('btn-change').addEventListener('click', function () {
  // 生成 newVnode
  var newVnode = h('ul#list', {}, [
    h('li.item', {}, 'Item 1'),
    h('li.item', {}, 'Item B'),
    h('li.item', {}, 'Item 3')
  ])
  patch(vnode, newVnode) // 找出差异,渲染差异
})
// jquery例子改造
var snabbdom = window.snabbdom
// 定义关键函数 patch
var patch = snabbdom.init([
  snabbdom_class,
  snabbdom_props,
  snabbdom_style,
  snabbdom_eventlisteners
])

// 定义关键函数 h
var h = snabbdom.h

// 原始数据
var data = [{
    name: '张三',
    age: '20',
    address: '北京'
  },
  {
    name: '李四',
    age: '21',
    address: '上海'
  },
  {
    name: '王五',
    age: '22',
    address: '广州'
  }
]
// 把表头也放在 data 中
data.unshift({
  name: '姓名',
  age: '年龄',
  address: '地址'
})

var container = document.getElementById('container')

// 渲染函数
var vnode

function render(data) {
  var newVnode = h('table', {}, data.map(function (item) {
    var tds = []
    var i
    for (i in item) {
      if (item.hasOwnProperty(i)) {
        tds.push(h('td', {}, item[i] + ''))
      }
    }
    return h('tr', {}, tds)
  }))

  if (vnode) {
    // re-render
    patch(vnode, newVnode)
  } else {
    // 初次渲染
    patch(container, newVnode)
  }

  // 存储当前的 vnode 结果
  vnode = newVnode
}

// 初次渲染
render(data)

var btnChange = document.getElementById('btn-change')
btnChange.addEventListener('click', function () {
  data[1].age = 30
  data[2].address = '深圳'
  // re-render
  render(data)
})

核心API:

了解diff算法吗

// diff 算法实现
// code demo
function createElement(vnode) {
  var tag = vnode.tag // 'ul'
  var attrs = vnode.attrs || {}
  var children = vnode.children || []
  if (!tag) {
    return null
  }

  // 创建真实的 DOM 元素
  var elem = document.createElement(tag)
  // 属性
  var attrName
  for (attrName in attrs) {
    if (attrs.hasOwnProperty(attrName)) {
      // 给 elem 添加属性
      elem.setAttribute(attrName, attrs[attrName])
    }
  }
  // 子元素
  children.forEach(function (childVnode) {
    // 给 elem 添加子元素
    elem.appendChild(createElement(childVnode)) // 递归
  })

  // 返回真实的 DOM 元素
  return elem
}

// vnode newVnode compare
function updateChildren(vnode, newVnode) {
  var children = vnode.children || []
  var newChildren = newVnode.children || []

  children.forEach(function (childVnode, index) {
    var newChildVnode = newChildren[index]
    if (childVnode.tag === newChildVnode.tag) {
      // 深层次对比,递归
      updateChildren(childVnode, newChildVnode)
    } else {
      // 替换
      replaceNode(childVnode, newChildVnode)
    }
  })
}

function replaceNode(vnode, newVnode) {
  var elem = vnode.elem // 真实的 DOM 节点
  var newElem = createElement(newVnode)

  // 替换
}

answer:

MVVM(Vue)

说一下使用 jQuery 和使用框架的区别

todolist

// jquery
// html

// <div>
//   <input type="text" name="" id="txt-title">
//   <button id="btn-submit">submit</button>
// </div>
// <div>
//   <ul id="ul-list"></ul>
// </div>
var $txtTitle = $('#txt-title')
var $btnSubmit = $('#btn-submit')
var $ulList = $('#ul-list')
$btnSubmit.click(function () {
  var title = $txtTitle.val()
  if (!title) {
    return
  }
  var $li = $('<li>' + title + '</li>')
  $ulList.append($li)
  $txtTitle.val('')
})

// vue

// <div id="app">
//   <div>
//     <input v-model="title">
//     <button v-on:click="add">submit</button>
//   </div>
//   <div>
//     <ul>
//       <li v-for="item in list">{{item}}</li>
//     </ul>
//   </div>
// </div>

// data 独立
var data = {
  title: '',
  list: []
}
// 初始化 Vue 实例
var vm = new Vue({
  el: '#app',
  data: data,
  methods: {
    add: function () {
      this.list.push(this.title)
      this.title = ''
    }
  }
})
/*

with(this){  // this 就是 vm
    return _c(
        'div',
        {
            attrs:{"id":"app"}
        },
        [
            _c(
                'div',
                [
                    _c(
                        'input',
                        {
                            directives:[
                                {
                                    name:"model",
                                    rawName:"v-model",
                                    value:(title),
                                    expression:"title"
                                }
                            ],
                            domProps:{
                                "value":(title)
                            },
                            on:{
                                "input":function($event){
                                    if($event.target.composing)return;
                                    title=$event.target.value
                                }
                            }
                        }
                    ),
                    _v(" "),
                    _c(
                        'button',
                        {
                            on:{
                                "click":add
                            }
                        },
                        [_v("submit")]
                    )
                ]
            ),
            _v(" "),
            _c('div',
                [
                    _c(
                        'ul',
                        _l((list),function(item){return _c('li',[_v(_s(item))])})
                    )
                ]
            )
        ]
    )
}

*/

区别:

说一下对 MVVM 的理解

MVC:

MVVM:

关于 ViewModel

解答:

MVVM三要素:

// var obj = {
//     name: 'zhangsan',
//     age: 25
// }
// console.log(obj)

// var obj = {}
// var _name = 'shangsan'
// Object.defineProperty(obj, 'name', {
//     get: function () {
//         console.log('get', _name) // 监听
//         return _name
//     },
//     set: function (newVal) {
//         console.log('set', newVal)  // 监听
//         _name = newVal
//     }
// })

// var vm = new Vue({
//     el: '#app',
//     data: {
//         name: 'zhangsan',
//         age: 20
//     }
// })

var vm = {}
var data = {
  name: 'zhangsan',
  age: 20
}

var key, value
for (key in data) {
  (function (key) { // 保证 key 的独立作用域
    Object.defineProperty(vm, key, {
      get: function () {
        console.log('get', data[key]) // 监听
        return data[key]
      },
      set: function (newVal) {
        console.log('set', newVal) // 监听
        data[key] = newVal
      }
    })
  })(key)
}

答案:

根据 todo-list demo 的 render 函数:

render函数解决了模板中“逻辑”(v-for v-if)的问题 还剩下模板生成 html 的问题 另外,vm._c 是什么?render 函数返回了什么?

// html
// <div id="app">
//   <p>{{price}}</p>
// </div>

// js
var vm = new Vue({
  el: '#app',
  data: {
    price: 100
  }
})

// 以下是手写的 ppt 中的 render 函数
function render() {
  with(this) { // this 就是 vm
    return _c(
      'div', {
        attrs: {
          'id': 'app'
        }
      }, [
        _c('p', [_v(_s(price))])
      ]
    )
  }
}

function render1() {
  return vm._c(
    'div', {
      attrs: {
        'id': 'app'
      }
    }, [
      vm._c('p', [vm._v(vm._s(vm.price))])
    ]
  )
}

vue 的整个实现流程

组件化 React

说一下对组件化的理解

JSX 本质是什么

// jsx test
// devDependencies: babel-plugin-transform-react-jsx
// .babelrc
// {"plugins": ["transform-react-jsx"]}

// demo.js

/* @jsx h */
// 上面这段注释,用于更改 React.cerateElement()函数名称
// var profile = <div>
//   <img src="avatar.png" className="profile" />
//   <h3>{[user.firstName, user.lastName].join(' ')}</h3>
// </div>;

// class Input extends Component {
//   render() {
//     return (
//       <div>
//           <input value={this.state.title} onChange={this.changeHandle.bind(this)}/>
//           <button onClick={this.clickHandle.bind(this)}>submit</button>
//       </div>
//     );
//   }
// }

// 命令行输入:babel --plugins trnasform-react-jsx demo.jsx

JSX 和 vdom 的关系

说一下 setState 的过程

阐述自己对 React 和 vue 的认识

问题解答:

hybrid

hybrid 是什么,为何用 hybrid?

介绍一下 hybrid 更新和上线的流程?

hybrid 和 h5 的主要区别

前端 JS 和客户端如何通讯?

微信JSDK 20180815105719

// schema 使用的封装

// html
// <button id="btn1">扫一扫</button>
// <button id="btn2">分享</button>

// <script type="text/javascript" src="./invoke.js"></script>

(function(window, undefined) {

  // 调用 schema 的封装
  function _invoke(action, data, callback) {
    // 拼装 schema 协议
    var schema = 'myapp://utils/' + action
    schema += schema.indexOf('?') === -1 ? '?' : '&'
      // 拼接参数
    for (var key in data) {
      if (data.hasOwnProperty(key)) {
        schema += key + '=' + data[key] + '&'
      }
    }
      // 处理 callback
    callbackName = action + Date.now()
    window[callbackName] = callback
    schema += 'callback=' + callbackName

    // 触发
    var iframe = document.createElement('iframe')
    iframe.style.display = 'none'
    iframe.src = schema // 重要!
    var body = document.body
    body.appendChild(iframe)
    setTimeout(function() {
      body.removeChild(iframe)
      iframe = null
    })
  }

  // 暴露到全局变量
  window.invoke = {
    share: function(data, callback) {
      _invoke('share', data, callback)
    },
    scan: function(data, callback) {
      _invoke('scan', data, callback)
    }
    login: function(data, callback) {
      _invoke('login', data, callback)
    }
  }

})(window)

你热爱编程吗