揭秘一线互联网企业 前端JavaScript高级面试

揭秘一线互联网企业 前端JavaScript高级面试

第1章 课程介绍

1-1 导学

(框架介绍&)

1-2 架构

(同步和异步混合调用的顺序性&原型模式&虚拟DOM&MVVM&JSX&自定义bridge网络&)

第2章 ES6 语法

2-1 开始

(ES6标准入门&JavaScript 模块化方案 &打包&Promise&)

ES6标准入门,开发环境把es6编译成es5,

揭秘一线互联网企业 前端JavaScript高级面试_第1张图片

2-2 模块化 - 语法

(使用import语句导入模块&export命令)

export命令,输export default只默认输出一个,引入直接写文件名称即可,export好几个需要有大括号具体的使用

揭秘一线互联网企业 前端JavaScript高级面试_第2张图片

揭秘一线互联网企业 前端JavaScript高级面试_第3张图片

使用import语句导入模块,标准迭代越变越简单,大括号括起来fn1和fn2的两个函数,直接正常使用就可以了

揭秘一线互联网企业 前端JavaScript高级面试_第4张图片

揭秘一线互联网企业 前端JavaScript高级面试_第5张图片

util1.js

export default {
    a: 100
}

util2.js

export function fn1() {
    alert('fn1')
}

export function fn2() {
    alert('fn2')
}

index.js

default不用写大括号,其他的必须要加大括号

import util1 from './util1.js'
import { fn1, fn2 } from './util2.js'

console.log(util1)
fn1()
fn2()

// [1, 2, 3].map(item => item + 1)

2-3 模块化 - babel-new-part1

(&init&用 easy_install 安装&配置文件.babelrc&命令行转码babel-cli&babel-register& babel-core )

&init&用 easy_install 安装&配置文件.babelrc&命令行转码babel-cli&babel-register& babel-core

Js的编译器

揭秘一线互联网企业 前端JavaScript高级面试_第6张图片

安装babel依赖,创建.babelrc隐藏文件,安装babel-cli界面工具,之后命令行使用babel-version命令判断是否安装成功

揭秘一线互联网企业 前端JavaScript高级面试_第7张图片

创建index.js文件

命令行定位到src目录,下载安装node,

揭秘一线互联网企业 前端JavaScript高级面试_第8张图片

定位之后,下载安装node

揭秘一线互联网企业 前端JavaScript高级面试_第9张图片

进入目录查看node版本

在目录下运行npm init,输入信息就有了package.json文件,只用输入name其他回车。

揭秘一线互联网企业 前端JavaScript高级面试_第10张图片

安装babel-core和babel-preset,通过镜像下载

 

安装成功后创建.baberic文件,安装的两个插件的别名

揭秘一线互联网企业 前端JavaScript高级面试_第11张图片

windows管理员打开控制台,全局安装bable-cli

.babelrc

{
    "presets": ["es2015", "latest"],
    "plugins": []
}

2-4 模块化 - babel-new-part2

(&在线转换)

&在线转换

Babel可以把函数解析为普通函数,成为浏览器可以识别的语法

查看是否转化为一般函数

揭秘一线互联网企业 前端JavaScript高级面试_第12张图片

2-5 模块化 - webpack

(webpack 的特点与优势&安装&使用 loader&配置文件&使用 plugin&安装webpack与webpack-dev-server &将一个Node.js封装模块发布到NPM注册表&net.Server对象&entry)

webpack 的特点与优势&安装&使用 loader&配置文件&使用 plugin&安装webpack与webpack-dev-server &将一个Node.js封装模块发布到NPM注册表&net.Server对象&entry

Babel只是语法层面的解析,模块化的引用babel做不了。

揭秘一线互联网企业 前端JavaScript高级面试_第13张图片

保存到开发环境中,

揭秘一线互联网企业 前端JavaScript高级面试_第14张图片

Webpack配置文件符合commonJs规范

揭秘一线互联网企业 前端JavaScript高级面试_第15张图片

__dirname当前目录,module模块定义正则表达式规则测试,所有.js结尾除了node_modules目录自己写的文件,都要通过babel-loader编译

webpack.config.js

揭秘一线互联网企业 前端JavaScript高级面试_第16张图片

运行npm start就是运行webpack,根据配置的webpack.config.js,对js代码进行babel的编译输出到文件中。

揭秘一线互联网企业 前端JavaScript高级面试_第17张图片

Index.html引入bundle.js

揭秘一线互联网企业 前端JavaScript高级面试_第18张图片

到目录下启动静态的服务,

揭秘一线互联网企业 前端JavaScript高级面试_第19张图片

入口文件就是index.js,所有的js代码都被webpack打包到bundle.js文件中来。

揭秘一线互联网企业 前端JavaScript高级面试_第20张图片

揭秘一线互联网企业 前端JavaScript高级面试_第21张图片

webpack.config.js

除了node_modules都用babel-loader进行编译

module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname,
        filename: './build/bundle.js'
    },
    module: {
        rules: [{
            test: /\.js?$/,
            exclude: /(node_modules)/,
            loader: 'babel-loader'
        }]
    }
}

package.json

{
  "name": "webpack-wfp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-latest": "^6.24.1",
    "webpack": "^3.11.0"
  }
}

index.html




    
    Document


    

test

2-6 模块化 - rollup介绍

(Rollup&简介&安装&配置 )

Rollup&简介&安装&配置

Vue和React都是通过rollup.js打包的,都是面向es6的标准。Webpack违反单一职能原则

揭秘一线互联网企业 前端JavaScript高级面试_第22张图片

代码编译过程,

揭秘一线互联网企业 前端JavaScript高级面试_第23张图片

2-7 模块化 - rollup安装

(安装&命令&插件&下载一个基础镜像&配置文件.babelrc &配置&AMD&babel-core)

安装&命令&插件&下载一个基础镜像&配置文件.babelrc &配置&AMD&babel-core

Rollup模块化,初始npm环境,会生成package.json文件

揭秘一线互联网企业 前端JavaScript高级面试_第24张图片

安装rollup的插件

创建.babelrc的文件

揭秘一线互联网企业 前端JavaScript高级面试_第25张图片

引入babel和resolve插件,umd格式兼容script也兼容AMD和commonJS

揭秘一线互联网企业 前端JavaScript高级面试_第26张图片

揭秘一线互联网企业 前端JavaScript高级面试_第27张图片

安装babel-core再去运行npm start

揭秘一线互联网企业 前端JavaScript高级面试_第28张图片

2-8 模块化 - 总结

( ES6模块与CommonJS模块的差异& export default命令)

 ES6模块与CommonJS模块的差异& export default命令

2-9 class - JS构造函数

(什么是构造函数&构造函数模式&原型模式)

什么是构造函数&构造函数模式&原型模式

揭秘一线互联网企业 前端JavaScript高级面试_第29张图片

揭秘一线互联网企业 前端JavaScript高级面试_第30张图片

揭秘一线互联网企业 前端JavaScript高级面试_第31张图片

到目录下面启动静态的服务

揭秘一线互联网企业 前端JavaScript高级面试_第32张图片

index.js

构造函数通过prototype来增加add方法,所有的实例都会有add方法

// function MathHandle(x, y) {
//     this.x = x
//     this.y = y
// }

// MathHandle.prototype.add = function () {
//     return this.x + this.y
// }

// var m = new MathHandle(1, 2)
// // console.log(m.add())

// typeof MathHandle  // 'function'
// MathHandle.prototype.constructor === MathHandle  // true
// m.__proto__ === MathHandle.prototype  // true

// // 动物
// function Animal() {
//     this.eat = function () {
//         alert('Animal eat')
//     }
// }

// // 狗
// function Dog() {
//     this.bark = function () {
//         alert('Dog bark')
//     }
// }

// // 绑定原型,实现继承
// Dog.prototype = new Animal()

// var hashiqi = new Dog()
// hashiqi.bark()
// hashiqi.eat()

function fn() {
    console.log('real', this)  // real {a: 100}

    var arr = [1, 2, 3]
    arr.map(function (item) {
        console.log(this)  // window
    })
}
fn.call({a: 100})

// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}



    
    test


    

构造函数测试

2-10 class - 基本语法

(类的实例对象& Class表达式& constructor方法&)

类的实例对象& Class表达式& constructor方法&

Constructor构造器面向对象

揭秘一线互联网企业 前端JavaScript高级面试_第33张图片

原型里有constructor属性,等于构造函数本身

揭秘一线互联网企业 前端JavaScript高级面试_第34张图片

揭秘一线互联网企业 前端JavaScript高级面试_第35张图片

揭秘一线互联网企业 前端JavaScript高级面试_第36张图片

// import util1 from './util1.js'
// import { fn1, fn2 } from './util2.js'

// console.log(util1)
// fn1()
// fn2()

// [1, 2, 3].map(item => item + 1)

// class MathHandle {
//     constructor(x, y) {
//         this.x = x
//         this.y = y
//     }
//     add() {
//         return this.x + this.y
//     }
// }

// const m = new MathHandle(1, 2)

// console.log(typeof MathHandle)  // 'function'
// console.log(MathHandle.prototype.constructor === MathHandle)  // true
// console.log(m.__proto__ === MathHandle.prototype)  // true

// class Animal {
//     constructor(name) {
//         this.name = name
//     }
//     eat() {
//         alert(this.name + ' eat')
//     }
// }

// class Dog extends Animal {
//     constructor(name) {
//         super(name)   // 注意 !!!
//         this.name = name
//     }
//     say() {
//         alert(this.name + ' say')
//     }
// }

// const dog = new Dog('哈士奇')
// dog.say()
// dog.eat()

// function fn() {
//     console.log('real', this)  // real {a: 100}

//     var arr = [1, 2, 3]
//     arr.map(item => {
//         console.log(this)  // {a: 100}
//     })
// }
// fn.call({a: 100})

// real {a: 100}
// {a: 100}
// {a: 100}
// {a: 100}

import 'babel-polyfill'

function loadImg(src) {
    var promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
            resolve(img)
        }
        img.onerror = function () {
            reject('图片加载失败')
        }
        img.src = src
    })
    return promise
}

var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'

const load = async function () {
    const result1 = await loadImg(src1)
    console.log(result1)
    const result2 = await loadImg(src2)
    console.log(result2)
}
load()

2-11 class - 继承

(类的prototype属性和 __proto__ 属性&extends的继承目标&super对象)

类的prototype属性和 __proto__ 属性&extends的继承目标&super对象

Dog的构造函数显示原型是animal的实例,实现继承,dog也有了eat的方法。

揭秘一线互联网企业 前端JavaScript高级面试_第37张图片

SUPER()先执行animal的构造器,

揭秘一线互联网企业 前端JavaScript高级面试_第38张图片

转到刚刚目录

揭秘一线互联网企业 前端JavaScript高级面试_第39张图片

揭秘一线互联网企业 前端JavaScript高级面试_第40张图片

Crtl+c停止服务

揭秘一线互联网企业 前端JavaScript高级面试_第41张图片

2-12 class - 总结

(绑定class的几种方式&)

绑定class的几种方式&

2-13 promise-callback-hell

(callback调用&异步调用)

callback调用&异步调用

所有的异步都有回调函数,loadImg过程

揭秘一线互联网企业 前端JavaScript高级面试_第42张图片

揭秘一线互联网企业 前端JavaScript高级面试_第43张图片

揭秘一线互联网企业 前端JavaScript高级面试_第44张图片

重新打包一下

2-14 promise - 语法

(Promise的含义&Promise.resolve()&Promise.reject() & Promise.prototype.then() )

Promise的含义&Promise.resolve()&Promise.reject() & Promise.prototype.then()

最后return promise实例,onload执行resolve函数和onerror执行reject函数

揭秘一线互联网企业 前端JavaScript高级面试_第45张图片

过程分开了,一个then干一件事,then第一个参数是成功时的回调,第二个参数是失败

揭秘一线互联网企业 前端JavaScript高级面试_第46张图片

很多个then分批进行处理

揭秘一线互联网企业 前端JavaScript高级面试_第47张图片

重新编译一遍

2-15 promise - 总结

(Promise 功能介绍&)

Promise 功能介绍&

2-16 常用功能演示q

(let&const &模板模式详解&解构赋值&块级作用域&默认参数值 &箭头函数&this关键字)

let&const &模板模式详解&解构赋值&块级作用域&默认参数值 &箭头函数&this关键字

Let/const 定义变量可以重新赋值,const定义常量不能重新赋值,箭头函数解决this指向全局window的情况

揭秘一线互联网企业 前端JavaScript高级面试_第48张图片

揭秘一线互联网企业 前端JavaScript高级面试_第49张图片

要用反引号才能识别${},省略了+号的拼接

揭秘一线互联网企业 前端JavaScript高级面试_第50张图片

同时拿到a和c两个属性,取出数组和对象中的元素

揭秘一线互联网企业 前端JavaScript高级面试_第51张图片

jsfor里面定义的外面可以访问的到,var item一定要放到括号里面否则外面也能访问到,letfor里面定义的变量外面访问不到

揭秘一线互联网企业 前端JavaScript高级面试_第52张图片

默认参数,如果不传递就是0

揭秘一线互联网企业 前端JavaScript高级面试_第53张图片

函数只有一个参数并且函数体只有一句话,如果有两个参数以上并且有多行

揭秘一线互联网企业 前端JavaScript高级面试_第54张图片

fn()变为this,普通函数里执行this是window对象,箭头函数让this还是a对象

揭秘一线互联网企业 前端JavaScript高级面试_第55张图片

揭秘一线互联网企业 前端JavaScript高级面试_第56张图片

揭秘一线互联网企业 前端JavaScript高级面试_第57张图片

揭秘一线互联网企业 前端JavaScript高级面试_第58张图片

2-17 常用功能 - 代码演示

(const&for..of循环&Map&使用apply或call实现伪继承&)

const&for..of循环&Map&使用apply或call实现伪继承&

// ES6 其他常用功能

// var arr = [1, 2, 3];
// arr.map(function (item) {
//     return item + 1;
// })

const arr = [1, 2, 3];
arr.map(item => item + 1);
arr.map((item, index) => {
    console.log(item);
    return item + 1;
});

2-18 常用功能 - 总结

(常用功能 - 总结&)

常用功能 - 总结&

第3章  -原型

3-1 开始

(使用原型对象模式&)

3-2 实际应用 - jQuery使用

(添加CSS代码到样式视图&通过html()方法判断元素是否为空&)

3-3 实际应用 - Zepto - 1

(ArrayBuffer.prototype.slice() &使用apply或call实现伪继承&new绑定&[[Prototype]]&以工厂函数取代构造函数(Replace Constructor with Factory Function) )

3-4 实际应用 - Zepto - 2

(组合使用构造函数模式和原型模式)

3-5 实际应用 - Zepto - 3

(使用CSS()方法直接设置元素样式值&)

3-6 实际应用 - jQuery-1

(使用new关键字调用构造器创建对象&this和对象原型 )

3-7 实际应用 - jQuery-2

(Reflect.apply(func, thisArg, args)&前端框架介绍——jQuery&slice()方法&prototype的作用域&)

3-8 实际应用 - 总结

(_proto__ 属性、Object.setPrototypeOf()、Object.getPrototypeOf()&)

3-9 扩展性 - 插件机制

(插件机制&构造函数模式&原型模式)

3-9 扩展性 - 总结

(插件机制&)

3-10 扩展性 - 代码演示

(Window对象的属性和方法)

3-10 总结

第4章 -异步

4-1 开始

(轮询&异步IO模型(Asynchronous IO)&Promise/Deferred模式&Promise&await()方法&)

轮询&异步IO模型(Asynchronous IO)&Promise/Deferred模式&Promise&await()方法&

4-2 单线程 - 介绍-1

(单线程程序&DOM渲染&)

单线程程序&DOM渲染&

单线程同一时间只能做一件事情,浏览器渲染DOM页面,

揭秘一线互联网企业 前端JavaScript高级面试_第59张图片

揭秘一线互联网企业 前端JavaScript高级面试_第60张图片

揭秘一线互联网企业 前端JavaScript高级面试_第61张图片

4-3 单线程 - 介绍-2

(Alert应用程序编程接口&)

Alert应用程序编程接口&

ajax 在success成功之后放到异步队列,setTimeout在1000毫秒后放到异步队列中,打印d a c b



    
    Document


    

async test

4-4 单线程 - 异步-1

(DOM渲染&同步与异步)

DOM渲染&同步与异步

揭秘一线互联网企业 前端JavaScript高级面试_第62张图片

揭秘一线互联网企业 前端JavaScript高级面试_第63张图片

4-5 单线程 - 异步-2

(setTimeout函数与clearTimeout函数&Ajax在jQuery中的应用&)

setTimeout函数与clearTimeout函数&Ajax在jQuery中的应用&

4-6 单线程 - 总结

( callback调用&异步执行&)

callback调用&异步执行&

Callback回过头来执行,success、setTimeout函数就是callback异步完事之后要执行的函数。

4-7 event-loop - 演示

(轮询&)

轮询&

异步通过event-loop实现的

揭秘一线互联网企业 前端JavaScript高级面试_第64张图片

揭秘一线互联网企业 前端JavaScript高级面试_第65张图片

有延时100毫秒之后放到异步队列

揭秘一线互联网企业 前端JavaScript高级面试_第66张图片

4-8 event-loop - 代码演示

(Ajax&)

Ajax&

4-9 event-loop - 总结

(定义模型和同步&)

定义模型和同步&

4-10 jquery-deferred - 介绍

(延迟处理——Deferred对象和Promise&)

延迟处理——Deferred对象和Promise&

4-11 jquery-deferred - 介绍演示

(done()&链式调用then方法&)

done()&链式调用then方法&

4-12 jquery-deferred - 应用-1

(延迟处理——Deferred对象和Promise&)

延迟处理——Deferred对象和Promise&

4-13 jquery-deferred - 应用-2

(Promise/Deferred模式&resolve&)

Promise/Deferred模式&resolve&

4-14 jquery-deferred - 应用-3

(then(..) 和catch(..)&Promise.resolve(..) 和Promise.reject(..)&)

then(..) 和catch(..)&Promise.resolve(..) 和Promise.reject(..)&

4-15 jquery-deferred - 应用-4

(when方法&)

when方法&

4-16 jquery-deferred - 总结

(实例封闭&)

实例封闭&

4-17 promise - 语法回顾

(Promise.prototype.catch()&Promise.prototype.then()& Promise.all()&Promise.race()&Promise.resolve()&Promise.reject()&基本用法&for、let和return子句)

Promise.prototype.catch()&Promise.prototype.then()& Promise.all()&Promise.race()&Promise.resolve()&Promise.reject()&基本用法&for、let和return子句

Promise.all请求全部完成后再去执行其他工作,promise.race只要有一个完成就去执行其他工作,

揭秘一线互联网企业 前端JavaScript高级面试_第67张图片

进入有html目录,高级浏览器原生就支持promise,老版的浏览器用bluebird

揭秘一线互联网企业 前端JavaScript高级面试_第68张图片

揭秘一线互联网企业 前端JavaScript高级面试_第69张图片

Img.onload说明image已经加载成功,img.onerror已经失败了,成功回调resolve要传入img,function(img){ },错误回调没有参数,链式操作,return img第二个then才能接受到

揭秘一线互联网企业 前端JavaScript高级面试_第70张图片

揭秘一线互联网企业 前端JavaScript高级面试_第71张图片

4-18 promise - 捕获异常-1

(Promise.prototype.catch()&可选的return(..)和throw(..) )

Promise.prototype.catch()&可选的return(..)和throw(..)

Then只接收成功回调函数,失败回调函数统一由catch接收,

揭秘一线互联网企业 前端JavaScript高级面试_第72张图片

自定义错误throw,catch捕获异常,throw是逻辑之外的错误

揭秘一线互联网企业 前端JavaScript高级面试_第73张图片

揭秘一线互联网企业 前端JavaScript高级面试_第74张图片

Catch捕获reject中的内容

揭秘一线互联网企业 前端JavaScript高级面试_第75张图片

4-19 promise - 捕获异常-2

(Promise.resolve(..) 和Promise.reject(..)&可选的return(..)和throw(..)&)

Promise.resolve(..) 和Promise.reject(..)&可选的return(..)和throw(..)&

4-20 promise - 串联

(串联过滤器&)

串联过滤器&

希望先加载第一个之后再加载第二个,不一块加载,拿到用户id之后再去做其他事。

第一个回调函数要return result2再去then,第二个then监听result2的成功回调函数

揭秘一线互联网企业 前端JavaScript高级面试_第76张图片

揭秘一线互联网企业 前端JavaScript高级面试_第77张图片

分顺序串联执行起来的,

揭秘一线互联网企业 前端JavaScript高级面试_第78张图片

4-21 promise-all-race

(Promise.all() & Promise.race())

Promise.all() & Promise.race()

All接收一个数组,result1和result2两个图片链接的返回,datas[0]代表两个promise的返回结果。多个promise同时执行完毕之后再去执行then方法。

Race是一个promise的返回值,只要有一个成功then执行。

揭秘一线互联网企业 前端JavaScript高级面试_第79张图片

揭秘一线互联网企业 前端JavaScript高级面试_第80张图片

4-22 promise - 标准总结

(Promise&)

Promise&

Pending loadFile刚刚传入src还没有加载,fulfilled成功,rejected失败。

揭秘一线互联网企业 前端JavaScript高级面试_第81张图片

揭秘一线互联网企业 前端JavaScript高级面试_第82张图片

揭秘一线互联网企业 前端JavaScript高级面试_第83张图片

4-23 promise - 总结

(Error对象&Promise.all()&Promise.race()&Promise.reject() )

Error对象&Promise.all()&Promise.race()&Promise.reject()




    
    Document


    

promise test

datas是两个promise返回结果,resolve成功后传入的img

揭秘一线互联网企业 前端JavaScript高级面试_第84张图片

4-24 async-await-1

(async函数&async 和 await&)

async函数&async 和 await&

揭秘一线互联网企业 前端JavaScript高级面试_第85张图片

同步写法解决异步问题,函数前面必须有async,await后面必须跟promise实例

揭秘一线互联网企业 前端JavaScript高级面试_第86张图片

要兼容

Load()正常函数调用

揭秘一线互联网企业 前端JavaScript高级面试_第87张图片

揭秘一线互联网企业 前端JavaScript高级面试_第88张图片

揭秘一线互联网企业 前端JavaScript高级面试_第89张图片

Webpack的打包命令

测试打包的静态代码

揭秘一线互联网企业 前端JavaScript高级面试_第90张图片

4-25 async-await-2

(用<s:actionerror>标签显示全部出错信息&Promise对象&)

用<s:actionerror>标签显示全部出错信息&Promise对象&

result1和result2是resolve成功回调传入的img标签

// import util1 from './util1.js'
// import { fn1, fn2 } from './util2.js'

// console.log(util1)
// fn1()
// fn2()

// // [1, 2, 3].map(item => item + 1)

import 'babel-polyfill'

function loadImg(src) {
    var promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
            resolve(img)
        }
        img.onerror = function () {
            reject('图片加载失败')
        }
        img.src = src
    })
    return promise
}

var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'

const load = async function () {
    const result1 = await loadImg(src1)
    console.log(result1)
    const result2 = await loadImg(src2)
    console.log(result2)
}
load()

4-26 async-await - 总结

(基于Promise对象的自动执行&)

基于Promise对象的自动执行&

4-27 总结

第5章 虚拟 DOM

5-1 开始

(什么是虚拟DOM&结论与虚拟DOM Diff算法&)

什么是虚拟DOM&结论与虚拟DOM Diff算法&

5-2 什么是 vdom - 开始

(什么是虚拟DOM)

什么是虚拟DOM

用js来模拟DOM结构,只有js能处理逻辑

揭秘一线互联网企业 前端JavaScript高级面试_第91张图片

揭秘一线互联网企业 前端JavaScript高级面试_第92张图片

揭秘一线互联网企业 前端JavaScript高级面试_第93张图片

5-3 什么是 vdom-jquery-1

(为什么要引入虚拟DOM&使用append()方法插入节点)

为什么要引入虚拟DOM&使用append()方法插入节点

揭秘一线互联网企业 前端JavaScript高级面试_第94张图片

页面加载完毕执行render,

揭秘一线互联网企业 前端JavaScript高级面试_第95张图片

浏览器最耗费性能的就是渲染

揭秘一线互联网企业 前端JavaScript高级面试_第96张图片

5-4 什么是 vdom-jquery-2

(使用Bootstrap&使用append()方法插入节点&)

使用Bootstrap&使用append()方法插入节点&

5-5 什么是 vdom - 总结

(createElement用法 &)

createElement用法 &

Js运行效率高,DOM操作是昂贵的

揭秘一线互联网企业 前端JavaScript高级面试_第97张图片

5-6 使用 vdom-snabbdom-1

(MVVM&前端组件化方案 )

MVVM&前端组件化方案

5-7 使用 vdom-snabbdom-2

(每当SNAPSHOT(快照)依赖被建立时都要进行构建&利用patch更新源代码&)

每当SNAPSHOT(快照)依赖被建立时都要进行构建&利用patch更新源代码&

5-8 使用 vdom-snabbdom-3

(SnapShotter&)

SnapShotter&

5-9 使用 vdom - 重做demo-1

( Shallow Render&addEventListener与attachEvent& getOwnPropertyDescriptor() &)

Shallow Render&addEventListener与attachEvent& getOwnPropertyDescriptor() &

5-10 使用 vdom - 重做demo-2

( 避免不必要的 render&)

避免不必要的 render&

5-11 使用 vdom - 总结

(PUT/PATCH/DELETE/HEAD&)

PUT/PATCH/DELETE/HEAD&

5-12 Diff算法 - 开始

(diff&结论与虚拟DOM Diff算法&)

5-13 Diff算法 - 命令演示

(Git Diff 魔法&)

5-14 Diff算法 - vdom 为何要使用 Diff

( git diff和提交范围&异曲同工的类(Alternative Classes with Different Interfaces))

5-15 Diff算法 - 实现-1

(筛选内容&结论与虚拟DOM Diff算法&)

5-16 Diff算法 - 实现-2

(children属性&使用append()方法插入节点&)

5-17 Diff算法 - 实现-3

(利用patch更新源代码&)

5-18 Diff算法 - 实现-4

(foreach循环优于for循环&SDIFF、SDIFFSTORE:对集合执行差集计算&)

5-19 Diff算法 - 总结

(diff&)

5-20 总结 - part1

(虚拟DOM&)

5-21 总结 - part2

(Git Diff 魔法&)

第6章 MVVM 和 vue

6-1 开始

(MVVM&Vue.js 是什么&响应式编程&视图和视图解析器&)

6-2 从jQuery到框架 - jQuery

(Todo应用实例&指令:ng-click &)

6-3 从jQuery到框架 - Vue

(v-for&v-model 修饰指令&事件处理函数中this关键字&new绑定&分离技术&驱动模型ModelDriven)

6-4 从jQuery到框架 - 区别

(应用服务和数据服务分离&数据绑定&Vue 实例方法&)

6-5 从jQuery到框架 - 总结

(逻辑分离例子&)

6-6 如何理解 MVVM - MVC

(MVVM&)

6-7 如何理解 MVVM - MVVM

(数据绑定&@click 缩写)

6-8 如何理解 MVVM - 总结

(从MVC到MVVM&)

6-9 Vue三要素

(事件处理函数中this关键字&响应式编程模型&实现一个模板引擎&条件渲染指)

6-10 响应式 - 介绍

(Undefined类型&VM 选项&字符串模板&实现视图模板&Render函数&with)

6-11 响应式 - defineProperty 演示

(defineProperty() &)

6-12 响应式 - 模拟

( getPrototypeOf() &)

6-13 响应式 - 总结

(源码篇——深入响应式原理&)

6-14 模板解析 - 开始

(定义模板&Render函数&)

6-15 模板解析 - 模板是什么

(XML配置文件模板&)

6-16 render函数 - with的用法

(使用replace()和replaceWith()方法替换内容&)

6-17 render函数 - 讲解1

(DOM Rendering&)

6-18 render函数 - 讲解2

(避免不必要的 render &)

6-19 render函数 - 讲解3

( v-if&组件和v-for&)

6-20 render函数 - 讲解4

(预编译JSP &前端组件化方案&v-model&)

6-21 render函数 - 讲解5

(源码篇——属性props&includes()、startsWith()、endsWith() &Render函数&)

6-22 render函数 - 讲解6

(endsWith函数&)

6-23 render函数 - 讲解7

(vm.$on&使用vm模块改变脚本运行环境& UPDATE语句&)

6-24 总结

(创建第一个模板工程&)

6-25 整体流程-1

(Vue.js 与其他框架的区别&模板解析事件&对信息进行监听和响应&)

6-26 整体流程-2

(vm.$set&vm.$watch&patch介绍&)

6-27 整体流程 - 总结

(Render函数&Spring Data数据访问模型&)

6-28 总结

(构建响应式消息通信组件&更换模板解析器&)

第7章 组件化 和 React

7-1 开始

(配置React开发环境 &)

配置React开发环境 &

7-2 todolist-demo-1

(初始化一个React项目&避免不必要的 render &import&constructor&整合&模板&CounterApp组件)

初始化一个React项目&避免不必要的 render &import&constructor&整合&模板&CounterApp组件

保证node和npm环境

全局安装create-react-app,创建react环境

创建空文件夹

揭秘一线互联网企业 前端JavaScript高级面试_第98张图片

Npm start 开发版本的server,build打包,test执行测试用例

揭秘一线互联网企业 前端JavaScript高级面试_第99张图片

进入react-test目录,启动3000端口,启动开发服务

编辑App.js即可。

揭秘一线互联网企业 前端JavaScript高级面试_第100张图片

揭秘一线互联网企业 前端JavaScript高级面试_第101张图片

Index.js入口文件,引入App组件

揭秘一线互联网企业 前端JavaScript高级面试_第102张图片

src下面新建component文件夹下新建index.js。props传递到父的class中,render组件往外渲染的函数

揭秘一线互联网企业 前端JavaScript高级面试_第103张图片

App.js引入component的js,在div中渲染ToDo的组件就可以了

揭秘一线互联网企业 前端JavaScript高级面试_第104张图片

揭秘一线互联网企业 前端JavaScript高级面试_第105张图片

揭秘一线互联网企业 前端JavaScript高级面试_第106张图片

7-3 todolist-demo-2

(定义应用的state&concat()方法&Render函数&初始化props&初始化data)&JS/JSX中的组件名大小写&Key的用法&onChange处理器&使用bind()方法绑定事件&Event&React的props和state)

定义应用的state&concat()方法&Render函数&初始化props&初始化data)&JS/JSX中的组件名大小写&Key的用法&onChange处理器&使用bind()方法绑定事件&Event&React的props和state

维护列表,state保存当前组件所有的变量,addTitle方法中往list添加title标题,参数就是title。

setState把list重新赋值。

揭秘一线互联网企业 前端JavaScript高级面试_第107张图片

列表渲染list,引用list组件,因为list组件中有props.data的属性,data的属性等于state的list。必须要有data属性才可以渲染

揭秘一线互联网企业 前端JavaScript高级面试_第108张图片

列表渲染list,需要新建list文件夹下新建index.js,list是传入进来的data,props下的data是期望别人传入的列表。

返回ul列表元素展示列表。大括号Jsx可以在html执行js表达式。所有遍历里面元素必须有个key

揭秘一线互联网企业 前端JavaScript高级面试_第109张图片

输入,需要建立和list同级的input组件,title是输入框中的值,默认空字符串,input监听change事件。输入框值的变化都会同步到state中去,

揭秘一线互联网企业 前端JavaScript高级面试_第110张图片

Bind(this) this是整个组件的实例,this.setState就可以使用重新赋值

点击clickHandler.bind(this)执行时this是当前组件的实例,点击button的时候需要state的最新值。

要把title添加进列表,addTitle是函数props属性title传入进来的,将title添加进addTitle回显。Value少了个a

揭秘一线互联网企业 前端JavaScript高级面试_第111张图片

toDo组件将Input引入进来,需要传递addTitle属性,点击之后会把title拼接到数组中

揭秘一线互联网企业 前端JavaScript高级面试_第112张图片

揭秘一线互联网企业 前端JavaScript高级面试_第113张图片

7-4 todolist-demo-3

( Render函数&使用data()方法存取普通数据&使用bind()方法绑定事件&getString()方法与getObject()方法有什么区别&遍历器对象的return()、throw()  )

Render函数&使用data()方法存取普通数据&使用bind()方法绑定事件&getString()方法与getObject()方法有什么区别&遍历器对象的return()、throw()

React必须要用构建方式去做,src/index.js 引用了 render出来了,App.js引用了Todo组件,Todo组件维护了list和addTitle方法,重新setState赋值。

输入框组件需要传递addTitle函数,

揭秘一线互联网企业 前端JavaScript高级面试_第114张图片

输入框随时改变随时修改title的值,把title传入到Todo组件的addTitle方法中,放入list中

揭秘一线互联网企业 前端JavaScript高级面试_第115张图片

7-5 todolist-demo-4

(创建控制层及index.jsp文件&state 状态 &state 设计原则&DOM 操作&基于 props 得到初始 state)

创建控制层及index.jsp文件&state 状态 &state 设计原则&DOM 操作&基于 props 得到初始 state

Vue视图与数据的分离,修改数据驱动Dom,没有直接修改Dom。

React一个组件的数据全部保存在state中,视图在render()中,

揭秘一线互联网企业 前端JavaScript高级面试_第116张图片

List组件没有数据,只是把toDo组件的数据显示出来而已,视图保持独立。

揭秘一线互联网企业 前端JavaScript高级面试_第117张图片

修改了list数据,来驱动力视图的变化,没有append到标签上

揭秘一线互联网企业 前端JavaScript高级面试_第118张图片

清空输入框,没有直接操作Dom,操作完数据,输入框自动清空

揭秘一线互联网企业 前端JavaScript高级面试_第119张图片

React与vue不同,react必须是组件化的必须有input和List组件,Vue是MVVM设计特点,数据与视图分离,操作数据驱动视图变化,输入框被清空。

index.js入口文件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

// patch(container, vnode)
ReactDOM.render(, document.getElementById('root'));
registerServiceWorker();

/*
    React.createElement(App, null);
    var app = new App()
    return app.render()
*/

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import Todo from './components/todo/index.js'

class App extends Component {
  render() {
    return (
      
); } } /* React.createElement( "div", null, React.createElement(Todo, null) ); var todo = new Todo() return todo.render() */ export default App;

component/index.js,组件化每一个组件都支持数据视图分离,数据驱动视图

import React, { Component } from 'react'
import Input from './input/index.js'
import List from './list/index.js'

// class Component {
//     constructor(props) {

//     }
//     renderComponent() {
//         const prevVnode = this._vnode
//         const newVnode = this.render()
//         patch(prevVnode, newVnode)
//         this._vnode = newVnode
//     }
// }

class Todo extends Component {
    constructor(props) {
        super(props)
        this.state = {
            list: ['a', 'b']
        }
    }
    render() {
        return (
            
) /* React.createElement( "div", null, React.createElement(Input, { addTitle: this.addTitle.bind(this) }), React.createElement(List, { data: this.state.list }) ); */ // React.createElement(List, { data: this.state.list }) // var list = new List({ data: this.state.list }) // var vnode = list.render() } addTitle(title) { const currentList = this.state.list this.setState({ list: currentList.concat(title) } // , () => { // // console.log(this.state.list) // this.renderComponent() // } ) } } export default Todo

input/index.js

import React, { Component } from 'react'

class Input extends Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ''
        }
    }
    render() {
        return (
            
) } changeHandle(event) { this.setState({ title: event.target.value }) } clickHandle() { const title = this.state.title const addTitle = this.props.addTitle addTitle(title) // 重点!!! this.setState({ title: '' }) } } export default Input

list/index.js react没有直接操作Dom,通过修改数据state驱动视图变化,数据视图分离state/render,修改完state视图立即发生了变化。

import React, { Component } from 'react'

class List extends Component {
    constructor(props) {
        super(props)
    }
    render() {
        const list = this.props.data
        return (
            
    { list.map((item, index) => { return
  • {item}
  • }) }
) /* React.createElement( "ul", null, list.map((item, index) => { return React.createElement( "li", { key: index }, item ); }) ); */ } } export default List

7-6 什么是组件 - 组件封装

(编写可复用组件&面向对象)

编写可复用组件&面向对象

组件化的理解封装与复用,组件封装好外面调用,外界不用关心视图,数据是什么,变化逻辑数据驱动视图变化,给我配合的数据列表,我完成功能就可以。

揭秘一线互联网企业 前端JavaScript高级面试_第120张图片

变化逻辑setState,外界谁引用这三块都不需要知道

揭秘一线互联网企业 前端JavaScript高级面试_第121张图片

7-7 什么是组件 - 组件复用 - 总结

(使用props传递数据&封装复用)

使用props传递数据&封装复用

复用需要传递props属性才能达到复用效果,传递不同props达到复用一个模板使用多次,每次传递不同数据即可。不然就是重复,props封装一次复用一万次

揭秘一线互联网企业 前端JavaScript高级面试_第122张图片

Li是内置的组件,返回一个li组件实例

揭秘一线互联网企业 前端JavaScript高级面试_第123张图片

揭秘一线互联网企业 前端JavaScript高级面试_第124张图片

7-8 JSX本质 - 语法演示

(有害的括号垃圾&)

有害的括号垃圾&

JSX注释先要大括号括起来,大括号显示js变量或者表达式,如果为zhangsan打印出来

揭秘一线互联网企业 前端JavaScript高级面试_第125张图片

name显示name,没name显示lisi

揭秘一线互联网企业 前端JavaScript高级面试_第126张图片

三元 show?如果show是false显示空字符串

揭秘一线互联网企业 前端JavaScript高级面试_第127张图片

通过循环生成列表

揭秘一线互联网企业 前端JavaScript高级面试_第128张图片

揭秘一线互联网企业 前端JavaScript高级面试_第129张图片

Js会把-转为大写,生成样式,大括号放js变量

揭秘一线互联网企业 前端JavaScript高级面试_第130张图片

大括号里放js变量和表达式,要有两个大括号,外层大括号存放里面js变量表达式,里面大括号自定义变量对象格式

揭秘一线互联网企业 前端JavaScript高级面试_第131张图片

Dom渲染

Class是js的保留字,必须要写className

揭秘一线互联网企业 前端JavaScript高级面试_第132张图片

7-9 JSX本质 - 解析成JS

(渲染函数和JSX&制作Component&JS/JSX中的组件名大小写&什么是Render函数&createElement用法 &)

渲染函数和JSX&制作Component&JS/JSX中的组件名大小写&什么是Render函数&createElement用法 &

揭秘一线互联网企业 前端JavaScript高级面试_第133张图片

揭秘一线互联网企业 前端JavaScript高级面试_第134张图片

7-10 JSX本质 - 标准

(渲染函数和JSX&JSX是进步还是倒退&)

渲染函数和JSX&JSX是进步还是倒退&

7-11 JSX本质 - 总结

(React&的JSX)

React&的JSX

// import Input from './input/index.js'
// import List from './list/index.js'

// function render() {
//     return (
//         
//

this is demo

// // //
// ) // } // var profile =
// //

{[user.firstName, user.lastName].join(' ')}

//
; // class Input extends Component { // render() { // return ( //
// // //
// ); // } // }
    { list.map((item, index) => { return
  • {item}
  • }) }

.babelrc

{"plugins": ["transform-react-jsx"]}

package.json

{
  "name": "jsx-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-plugin-transform-react-jsx": "^6.24.1"
  }
}

7-12 JSX 和 vdom - vdom 回顾

(patch介绍&新增节点&)

patch介绍&新增节点&

7-13 JSX 和 vdom - 何时patch

(VNode的类型&元素节点&函数式组件&DOM Rendering &)

VNode的类型&元素节点&函数式组件&DOM Rendering &

7-14 JSX 和 vdom - 自定义组件的处理

(Render函数& createElement用法& 函数化组件&基本参数&)

Render函数& createElement用法& 函数化组件&基本参数&

揭秘一线互联网企业 前端JavaScript高级面试_第135张图片

揭秘一线互联网企业 前端JavaScript高级面试_第136张图片

7-15 JSX 和 vdom - 示例演示

(什么是Render函数&export风格&)

什么是Render函数&export风格&

import React, { Component } from 'react'
import Input from './input/index.js'
import List from './list/index.js'

// class Component {
//     constructor(props) {

//     }
//     renderComponent() {
//         const prevVnode = this._vnode
//         const newVnode = this.render()
//         patch(prevVnode, newVnode)
//         this._vnode = newVnode
//     }
// }

class Todo extends Component {
    constructor(props) {
        super(props)
        this.state = {
            list: ['a', 'b']
        }
    }
    render() {
        return (
            
) /* React.createElement( "div", null, React.createElement(Input, { addTitle: this.addTitle.bind(this) }), React.createElement(List, { data: this.state.list }) ); */ // React.createElement(List, { data: this.state.list }) // var list = new List({ data: this.state.list }) // var vnode = list.render() } addTitle(title) { const currentList = this.state.list this.setState({ list: currentList.concat(title) } // , () => { // // console.log(this.state.list) // this.renderComponent() // } ) } } export default Todo
import React, { Component } from 'react'

class List extends Component {
    constructor(props) {
        super(props)
    }
    render() {
        const list = this.props.data
        return (
            
    { list.map((item, index) => { return
  • {item}
  • }) }
) /* React.createElement( "ul", null, list.map((item, index) => { return React.createElement( "li", { key: index }, item ); }) ); */ } } export default List
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import Todo from './components/todo/index.js'

class App extends Component {
  render() {
    return (
      
); } } /* React.createElement( "div", null, React.createElement(Todo, null) ); var todo = new Todo() return todo.render() */ export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

// patch(container, vnode)
ReactDOM.render(, document.getElementById('root'));
registerServiceWorker();

/*
    React.createElement(App, null);
    var app = new App()
    return app.render()
*/

7-16 JSX 和 vdom - 总结

(渲染函数和JSX&VNode&)

渲染函数和JSX&VNode&

揭秘一线互联网企业 前端JavaScript高级面试_第137张图片

7-17 setState - 异步-1

(React的state&组件的生命周期&异步操作的模式&单线程应用及异常)

React的state&组件的生命周期&异步操作的模式&单线程应用及异常

组件内部视图和逻辑的变化通过setState实现的,修改属性setState是异步的。

多次setState放在一起,考虑性能没必要每次都要渲染,最后渲染一次就够了。

揭秘一线互联网企业 前端JavaScript高级面试_第138张图片

揭秘一线互联网企业 前端JavaScript高级面试_第139张图片

用户看不到中间效果,只需要执行最后setState覆盖的效果。

揭秘一线互联网企业 前端JavaScript高级面试_第140张图片

7-18 setState - 异步-2

( constructor方法&React的props和state&)

 constructor方法&React的props和state&

7-19 setState - 回顾 vue 修改属性

(测试异步方法&普通服务端渲染& defineProperty()&使用SQLUpdateClause进行更新操作&)

测试异步方法&普通服务端渲染& defineProperty()&使用SQLUpdateClause进行更新操作&

Vue与React功能类型的框架,Vue的渲染流程

揭秘一线互联网企业 前端JavaScript高级面试_第141张图片

揭秘一线互联网企业 前端JavaScript高级面试_第142张图片

揭秘一线互联网企业 前端JavaScript高级面试_第143张图片

揭秘一线互联网企业 前端JavaScript高级面试_第144张图片

揭秘一线互联网企业 前端JavaScript高级面试_第145张图片

揭秘一线互联网企业 前端JavaScript高级面试_第146张图片

揭秘一线互联网企业 前端JavaScript高级面试_第147张图片

Data中属性title和list变化了,会被set监听到

Data中属性title和list变化了,会被set监听到

揭秘一线互联网企业 前端JavaScript高级面试_第148张图片

揭秘一线互联网企业 前端JavaScript高级面试_第149张图片

Set执行update重新渲染是异步的

揭秘一线互联网企业 前端JavaScript高级面试_第150张图片

7-20 setState - 过程

(渲染函数render&回调函数&箭头函数&活用模板方法模式及Callback&patch介绍)

渲染函数render&回调函数&箭头函数&活用模板方法模式及Callback&patch介绍

Patch新的node和老的node对比,setState异步一定会有回调函数,最后的结果要放到function()

揭秘一线互联网企业 前端JavaScript高级面试_第151张图片

揭秘一线互联网企业 前端JavaScript高级面试_第152张图片

This函数箭头函数可以将this指向外层作用域,setState执行完毕执行回调函数state发生变化。继承Component,最后要执行patch函数找到差一点渲染出来。

揭秘一线互联网企业 前端JavaScript高级面试_第153张图片

揭秘一线互联网企业 前端JavaScript高级面试_第154张图片

7-21 setState - 总结

(使用SQLUpdateClause进行更新操作)

使用SQLUpdateClause进行更新操作

7-22 总结

(前端组件化方案&前端的模块化和组件化&)

前端组件化方案&前端的模块化和组件化&

揭秘一线互联网企业 前端JavaScript高级面试_第155张图片

7-23 React 和 Vue对比

(MVVM模式&前端组件化方案&模板字符串&在JSX中使用JavaScript&分离技术
模板&state 设计原则&Virtual DOM&)

MVVM模式&前端组件化方案&模板字符串&在JSX中使用JavaScript&分离技术
模板&state 设计原则&Virtual DOM&

Vue模板各种指令v-onclick v-if v-for。JSX标准化其他的库可以拿JSX开发。模板分离更强倾向于vue。

揭秘一线互联网企业 前端JavaScript高级面试_第156张图片

揭秘一线互联网企业 前端JavaScript高级面试_第157张图片

揭秘一线互联网企业 前端JavaScript高级面试_第158张图片

揭秘一线互联网企业 前端JavaScript高级面试_第159张图片

揭秘一线互联网企业 前端JavaScript高级面试_第160张图片

第8章 hybrid

8-1 开始

( AppFooter组件&上线时间透明化&)

8-2 hybrid是什么 - 开始

(WebView组件&混合开发&AppRegistry&)

8-3 hybrid是什么 - webview

(WebView组件样式设置&)

8-4 hybrid是什么 - file协议

(设置log file size,控制检查点&使用 HTTP 协议&)

8-5 hybrid是什么 - 具体实现和总结

(为手机APP、PC、H5网页提供统一风格的API&构建登录的静态页面&)

8-6 hybrid更新流程

(静态页面的创建&管理server级别下的配置项&一个显示版本号的 Hello World&)

8-7 hybrid 和 h5 的比较

(为手机APP、PC、H5网页提供统一风格的API &)

8-8 JS和客户端通讯 - 开始

(什么是微信公众号&企业内部通讯录应用开发&)

8-9 JS和客户端通讯 - schema协议-1

(Information Schema&关于Schema的理解&)

8-10 JS和客户端通讯 - schema协议-2

(HTML 5增强的iframe元素&活用模板方法模式及Callback&)

8-11 JS和客户端通讯 - schema封装-1

(XMLSchema&schema 注解实战&)

8-12 JS和客户端通讯 - schema封装-2

(Window对象&ShareJoin&)

8-13 JS和客户端通讯 - 总结

( InvokeDemo&内置的服务方法&)

8-14 总结

(为手机APP、PC、H5网页提供统一风格的API&)

第9章 课程总结

9-1 不讲nodejs

(深入浅出 nodeJs&)

9-2 如何热爱编程

(编程规范&)

9-3 总结

(将原型图分割成不同组件&)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(面试&编程技巧&Demo)