├── js ├── es6 │ ├── promise.md │ ├── equal.jpg │ └── equalJson.md └── javascript-questions.md ├── countDown ├── colorui │ ├── components │ │ ├── cu-custom.wxss │ │ ├── cu-custom.json │ │ ├── cu-custom.wxml │ │ └── cu-custom.js │ └── animation.wxss ├── app.wxss ├── components │ └── circle │ │ ├── circle.json │ │ ├── circle.wxss │ │ ├── circle.wxml │ │ └── circle.js ├── .DS_Store ├── pages │ ├── main │ │ ├── main.json │ │ ├── main.wxss │ │ ├── main.wxml │ │ └── main.js │ └── index │ │ ├── index.json │ │ ├── index.wxss │ │ ├── index.wxml │ │ └── index.js ├── sitemap.json ├── app.json ├── utils │ └── util.js ├── project.config.json └── app.js ├── .DS_Store ├── img ├── tip-box.jpg ├── element-mask.jpg ├── element-size.png ├── sort-compare.png └── display-adjust.png ├── css-attribute ├── fixed.jpg ├── select.png ├── relative.jpg ├── selector.png ├── location.md └── selector.md ├── arithmetic └── twoNumber-sum.md ├── javascript思维导图.md ├── README.md ├── html └── html-questions.md └── css └── css-questions.md /js/es6/promise.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /countDown/colorui/components/cu-custom.wxss: -------------------------------------------------------------------------------- 1 | /* colorui/components/cu-custom.wxss */ -------------------------------------------------------------------------------- /countDown/app.wxss: -------------------------------------------------------------------------------- 1 | @import "colorui/main.wxss"; 2 | @import "colorui/icon.wxss"; 3 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/.DS_Store -------------------------------------------------------------------------------- /countDown/colorui/components/cu-custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /countDown/components/circle/circle.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "component": true 4 | } -------------------------------------------------------------------------------- /img/tip-box.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/img/tip-box.jpg -------------------------------------------------------------------------------- /js/es6/equal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/js/es6/equal.jpg -------------------------------------------------------------------------------- /countDown/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/countDown/.DS_Store -------------------------------------------------------------------------------- /countDown/pages/main/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "circle": "/components/circle/circle" 4 | } 5 | } -------------------------------------------------------------------------------- /countDown/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "circle": "/components/circle/circle" 4 | } 5 | } -------------------------------------------------------------------------------- /img/element-mask.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/img/element-mask.jpg -------------------------------------------------------------------------------- /img/element-size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/img/element-size.png -------------------------------------------------------------------------------- /img/sort-compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/img/sort-compare.png -------------------------------------------------------------------------------- /css-attribute/fixed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/css-attribute/fixed.jpg -------------------------------------------------------------------------------- /css-attribute/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/css-attribute/select.png -------------------------------------------------------------------------------- /img/display-adjust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/img/display-adjust.png -------------------------------------------------------------------------------- /css-attribute/relative.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/css-attribute/relative.jpg -------------------------------------------------------------------------------- /css-attribute/selector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengxuZ/js-react-css-html-interview-question/HEAD/css-attribute/selector.png -------------------------------------------------------------------------------- /countDown/components/circle/circle.wxss: -------------------------------------------------------------------------------- 1 | /* components/circle/circle.wxss */ 2 | .circle_box,.circle_draw{ position: relative; } 3 | .circle_bg{position: absolute;} -------------------------------------------------------------------------------- /countDown/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /js/es6/equalJson.md: -------------------------------------------------------------------------------- 1 | ## 如何判断两个obj(json)是否相等 2 | 3 | 有这样两个obj 4 | 5 | ``` 6 | let obj1 = { 7 | name:'zhx', 8 | age:18, 9 | sex:boy 10 | } 11 | 12 | let obj2 = { 13 | name:'zhx', 14 | age:18, 15 | sex:girl 16 | } 17 | ``` 18 | 19 | 如何判断他们是否相等? 20 | 21 | ![](./equal.jpg "fixed布局") 22 | -------------------------------------------------------------------------------- /countDown/components/circle/circle.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /countDown/pages/main/main.wxss: -------------------------------------------------------------------------------- 1 | /* pages/main/main.wxss */ 2 | .circle_txt{ 3 | padding-left: 10rpx; 4 | color: #fff; 5 | font-size: 36rpx; 6 | letter-spacing: 2rpx; 7 | } 8 | 9 | .circle_text{ 10 | position: absolute; 11 | left: 50%; 12 | top: 50%; 13 | transform: translate(-50%,-50%); 14 | } 15 | .circle_time{ 16 | color: #000; 17 | font-size: 32rpx; 18 | padding-left: 16rpx; 19 | } -------------------------------------------------------------------------------- /countDown/pages/main/main.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ColorUI 空白模板 4 | 5 | 6 | 7 | 11 | 12 | {{time}} s 13 | 14 | 15 | -------------------------------------------------------------------------------- /countDown/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/main/main", 4 | "pages/index/index", 5 | "components/circle/circle" 6 | ], 7 | "window": { 8 | "backgroundTextStyle": "light", 9 | "navigationBarBackgroundColor": "#fff", 10 | "navigationBarTitleText": "ColorUI", 11 | "navigationStyle": "custom", 12 | "navigationBarTextStyle": "white" 13 | }, 14 | "usingComponents": { 15 | "cu-custom": "/colorui/components/cu-custom" 16 | }, 17 | "sitemapLocation": "sitemap.json" 18 | } -------------------------------------------------------------------------------- /countDown/utils/util.js: -------------------------------------------------------------------------------- 1 | const formatTime = date => { 2 | const year = date.getFullYear() 3 | const month = date.getMonth() + 1 4 | const day = date.getDate() 5 | const hour = date.getHours() 6 | const minute = date.getMinutes() 7 | const second = date.getSeconds() 8 | 9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 10 | } 11 | 12 | const formatNumber = n => { 13 | n = n.toString() 14 | return n[1] ? n : '0' + n 15 | } 16 | 17 | module.exports = { 18 | formatTime: formatTime 19 | } 20 | -------------------------------------------------------------------------------- /css-attribute/location.md: -------------------------------------------------------------------------------- 1 | ### css定位有哪些,区别是什么? 2 | 3 | 属性值 | 描述 | 脱离文档流?| 是否可以使用top,left| 4 | --|--|--|-- 5 | static |默认值,默认位置没有定位|否|否| 6 | relative |相对于自身第一个位置定位|否|是| 7 | absolute |相对于离元素最近的设置了**绝对或相对定位**的父元素|是|是| 8 | fixed |对于浏览器窗口是固定位置|是|是| 9 | z-index|元素的堆叠顺序|否|否| 10 | sticky|相对定位和固定定位的混合(适用于吸顶效果) |是|是 11 | inherit|从父元素继承 position 属性的值。| 12 | 13 | 14 | --- 15 | 16 | ### 如何判断是否脱离文档流? 17 | - relative布局时,设置了left.但下方盒子并没有上移,说明上方盒子占据着位置。他们是同一个文档流。对比absolute布局 18 | 19 | ![](./relative.jpg "relative布局") 20 | 21 | - z-index布局时,下方盒子并没有上移,说明上方盒子占据着位置。他们是同一个文档流,对比fixed布局 22 | 23 | ![](./fixed.jpg "fixed布局") 24 | -------------------------------------------------------------------------------- /countDown/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | /*圆环进度条文字*/ 3 | .circle_info{ 4 | position: absolute; 5 | width: 100%; 6 | left: 50%; 7 | top: 50%; 8 | transform: translate(-50%,-50%); 9 | display: flex; 10 | align-items: center; 11 | justify-content: center 12 | } 13 | .circle_dot{ 14 | width:16rpx; 15 | height: 16rpx; 16 | border-radius: 50%; 17 | background-color: #fb9126; 18 | } 19 | .circle_txt{ 20 | padding-left: 10rpx; 21 | color: #fff; 22 | font-size: 36rpx; 23 | letter-spacing: 2rpx; 24 | } 25 | 26 | .circle_text{ 27 | position: absolute; 28 | left: 50%; 29 | top: 50%; 30 | transform: translate(-50%,-50%); 31 | } 32 | .circle_time{ 33 | color: #fff; 34 | font-size: 32rpx; 35 | padding-left: 16rpx; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /countDown/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | ColorUI 空白模板 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | {{txt}} 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | {{time}} s 26 | 27 | 28 | -------------------------------------------------------------------------------- /countDown/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": true, 9 | "postcss": true, 10 | "minified": true, 11 | "newFeature": true, 12 | "autoAudits": false 13 | }, 14 | "compileType": "miniprogram", 15 | "libVersion": "2.4.2", 16 | "appid": "wx78921672d8c14cb8", 17 | "projectname": "countDown", 18 | "debugOptions": { 19 | "hidedInDevtools": [] 20 | }, 21 | "isGameTourist": false, 22 | "simulatorType": "wechat", 23 | "simulatorPluginLibVersion": {}, 24 | "condition": { 25 | "search": { 26 | "current": -1, 27 | "list": [] 28 | }, 29 | "conversation": { 30 | "current": -1, 31 | "list": [] 32 | }, 33 | "game": { 34 | "currentL": -1, 35 | "list": [] 36 | }, 37 | "miniprogram": { 38 | "current": -1, 39 | "list": [] 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /countDown/colorui/components/cu-custom.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /css-attribute/selector.md: -------------------------------------------------------------------------------- 1 | ### css选择器有哪些以及它们的优先级? 2 | 3 | 选择器 | 名称 |描述 4 | --|--|-- 5 | #id|id选择器| 6 | .class|类选择器| 7 | a|元素选择器| 指的是元素标签如h1,div 8 | div,p|分组选择器|多个选择器同时满足对应规则 9 | a[href]|属性选择器|指的是**标签元素**并且**属性**相同的节点 10 | div p|后代选择器| 见下 11 | div>p|子元素选择器| 见下 12 | div+p|相邻选择器|相同父元素,并且是相邻元素 13 | a:hover|伪类选择器| 14 | *|通配符选择器|选择所有的元素标签 15 | 16 | --- 17 | ### 选择器优先级 18 | 19 | 内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器 20 | 21 | --- 22 | 23 | ### 后代选择器与子元素选择器区别 24 | 25 | 26 | ``` 27 |
28 | 我是span标签 29 |

段落

30 |

31 | 我是P元素下的span标签 32 |

33 |
34 | ``` 35 | 后代选择器 36 | ``` 37 | div span { 38 | color:red; 39 | } 40 | ``` 41 | ### 结果如图: 42 | ![](./selector.png "relative布局") 43 | 44 | 子元素选择器 45 | ``` 46 | div>span { 47 | color:red; 48 | } 49 | ``` 50 | 结果如图: 51 | 52 | ![](./select.png "relative布局") 53 | 54 | **结论:** 子元素选择器只选择元素标签的子元素不会选择孙子元素(嵌套中的span),而后代选择器会选择元素标签内的所有span元素 -------------------------------------------------------------------------------- /arithmetic/twoNumber-sum.md: -------------------------------------------------------------------------------- 1 | ## 两数求和 2 | 3 | **题目描述** 4 | 5 | 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 6 | 7 | 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 8 | 9 | **举例** 10 | ``` 11 | 给定 nums = [2, 7, 11, 15], target = 9 12 | 13 | 因为 nums[0] + nums[1] = 2 + 7 = 9 14 | 所以返回 [0, 1] 15 | ``` 16 | 17 | 方法一:暴力法 18 | ``` 19 | function(nums, target) { 20 | for(let i=0;i{ 35 | map.set(v,i) //将arr的健值放入Map 36 | }) 37 | for(let i=0;i { 12 | // 发送 res.code 到后台换取 openId, sessionKey, unionId 13 | } 14 | }) 15 | // 获取用户信息 16 | wx.getSetting({ 17 | success: res => { 18 | if (res.authSetting['scope.userInfo']) { 19 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 20 | wx.getUserInfo({ 21 | success: res => { 22 | // 可以将 res 发送给后台解码出 unionId 23 | this.globalData.userInfo = res.userInfo 24 | 25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 26 | // 所以此处加入 callback 以防止这种情况 27 | if (this.userInfoReadyCallback) { 28 | this.userInfoReadyCallback(res) 29 | } 30 | } 31 | }) 32 | } 33 | } 34 | }) 35 | // 获取系统状态栏信息 36 | wx.getSystemInfo({ 37 | success: e => { 38 | this.globalData.StatusBar = e.statusBarHeight; 39 | let custom = wx.getMenuButtonBoundingClientRect(); 40 | this.globalData.Custom = custom; 41 | this.globalData.CustomBar = custom.bottom + custom.top - e.statusBarHeight; 42 | } 43 | }) 44 | }, 45 | globalData: { 46 | userInfo: null 47 | } 48 | }) -------------------------------------------------------------------------------- /countDown/pages/main/main.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | //获取应用实例 3 | const app = getApp() 4 | Page({ 5 | data: { 6 | StatusBar: app.globalData.StatusBar, 7 | CustomBar: app.globalData.CustomBar, 8 | num: 100, 9 | step: null, 10 | time: null, 11 | stepTimer: null, 12 | }, 13 | stepInterval: function () { 14 | // 设置倒计时 定时器 15 | var n = this.data.num / 2 16 | this.stepTimer = setInterval(() => { 17 | if (this.data.num >= 0) { 18 | this.data.step = this.data.num / n; 19 | // 绘制彩色圆环进度条 20 | this.circle.drawCircle('circle_draw', 40, 4, this.data.step) 21 | if ((/(^[1-9]\d*$)/.test(this.data.num / 10))) { 22 | // 当时间为整数秒的时候 改变时间 23 | this.setData({ 24 | time: this.data.num / 10 25 | }); 26 | } 27 | this.data.num--; 28 | } else { 29 | this.setData({ 30 | time: 0 31 | }); 32 | clearInterval(this.stepTimer); 33 | } 34 | }, 100) 35 | }, 36 | changeTime: function () { 37 | // 先清除定时器 38 | clearInterval(this.stepTimer); 39 | // 计数器 还原到100 40 | this.setData({ 41 | num: 100 42 | }); 43 | // 重新开启倒计时 44 | this.stepInterval() 45 | // 触发自定义组件事件 46 | this._runEvent() 47 | }, 48 | onLoad: function () { 49 | }, 50 | onReady: function () { 51 | /*倒计时*/ 52 | // 获得circle组件 53 | this.circle = this.selectComponent("#circle"); 54 | // 绘制背景圆环 55 | this.circle.drawCircleBg('circle_bg', 40, 4) 56 | // 绘制彩色圆环 57 | this.stepInterval() 58 | }, 59 | 60 | _runEvent() { 61 | console.log(1111) 62 | }, 63 | }) 64 | -------------------------------------------------------------------------------- /countDown/components/circle/circle.js: -------------------------------------------------------------------------------- 1 | /* components/circle/circle.js */ 2 | Component({ 3 | options: { 4 | multipleSlots: true // 在组件定义时的选项中启用多slot支持 5 | }, 6 | properties: { 7 | bg: { // 属性名 8 | type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型) 9 | value: 'bg' // 属性初始值(可选),如果未指定则会根据类型选择一个 10 | }, 11 | draw: { 12 | type: String, 13 | value: 'draw' 14 | }, 15 | }, 16 | 17 | data: { /* 私有数据,可用于模版渲染 */ 18 | size: 0, /* 圆环盒子大小 size >= 2*x ( x 为canvas的绘制半径)*/ 19 | step: 1, 20 | num: 100 21 | }, 22 | methods: { 23 | /* 24 | * 有关参数 25 | * id : canvas 组件的唯一标识符 canvas-id 26 | * x : canvas 绘制圆形的半径 27 | * w : canvas 绘制圆环的宽度 28 | */ 29 | drawCircleBg: function (id, x, w) { 30 | // 设置圆环外面盒子大小 宽高都等于圆环直径 31 | this.setData({ 32 | size: 2 * x // 更新属性和数据的方法与更新页面数据的方法类似 33 | }); 34 | // 使用 wx.createContext 获取绘图上下文 ctx 绘制背景圆环 35 | var ctx = wx.createCanvasContext(id, this) 36 | ctx.setLineWidth(w / 2); 37 | ctx.setStrokeStyle('#20183b'); 38 | ctx.setLineCap('round') 39 | ctx.beginPath();//开始一个新的路径 40 | //设置一个原点(x,y),半径为r的圆的路径到当前路径 此处x=y=r 41 | ctx.arc(x, x, x - w, 0, 2 * Math.PI, false); 42 | ctx.stroke();//对当前路径进行描边 43 | ctx.draw(); 44 | }, 45 | drawCircle: function (id, x, w, step) { 46 | // 使用 wx.createContext 获取绘图上下文 context 绘制彩色进度条圆环 47 | var context = wx.createCanvasContext(id, this); 48 | // 设置渐变 49 | var gradient = context.createLinearGradient(2 * x, x, 0); 50 | gradient.addColorStop("0", "#2661DD"); 51 | gradient.addColorStop("0.5", "#40ED94"); 52 | gradient.addColorStop("1.0", "#5956CC"); 53 | context.setLineWidth(w); 54 | context.setStrokeStyle(gradient); 55 | context.setLineCap('round') 56 | context.beginPath();//开始一个新的路径 57 | // step 从0到2为一周 58 | context.arc(x, x, x - w, -Math.PI / 2, step * Math.PI - Math.PI / 2, false); 59 | context.stroke();//对当前路径进行描边 60 | context.draw() 61 | }, 62 | /* 内部私有方法建议以下划线开头 , 63 | * 自定义组件触发事件时,需要使用 triggerEvent 方法,指定事件名、detail对象和事件选项 */ 64 | _runEvent() { 65 | //触发自定义组件事件 66 | this.triggerEvent("runEvent") 67 | } 68 | }, 69 | 70 | // 生命周期函数,可以为函数,或一个在methods段中定义的方法名 71 | onReady: function () { 72 | } 73 | }) -------------------------------------------------------------------------------- /countDown/pages/index/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | //获取应用实例 3 | const app = getApp() 4 | 5 | Page({ 6 | data: { 7 | StatusBar: app.globalData.StatusBar, 8 | CustomBar: app.globalData.CustomBar, 9 | txt: "正在匹配中...", 10 | count: 0,//计数器,初始值为0 11 | maxCount: 100, // 绘制一个圆环所需的步骤 12 | countTimer: null,//定时器,初始值为null 13 | }, 14 | countInterval: function () { 15 | // 设置倒计时 定时器 假设每隔100毫秒 count递增+1,当 count递增到两倍maxCount的时候刚好是一个圆环( step 从0到2为一周),然后改变txt值并且清除定时器 16 | this.countTimer = setInterval(() => { 17 | if (this.data.count <= 2 * this.data.maxCount) { 18 | // 绘制彩色圆环进度条 19 | this.circle1.drawCircle('circle_draw1', 100, 8, this.data.count / this.data.maxCount) 20 | this.data.count++; 21 | } else { 22 | this.setData({ 23 | txt: "匹配成功" 24 | }); 25 | clearInterval(this.countTimer); 26 | } 27 | }, 100) 28 | }, 29 | onReady: function () { 30 | // 获得circle组件 31 | this.circle1 = this.selectComponent("#circle1"); 32 | // 绘制背景圆环 33 | this.circle1.drawCircleBg('circle_bg1', 100, 8) 34 | // 绘制彩色圆环 35 | // this.circle.drawCircle('circle_draw1', 100, 8, 2); 36 | this.countInterval() 37 | }, 38 | 39 | //事件处理函数 40 | bindViewTap: function() { 41 | wx.navigateTo({ 42 | url: '../logs/logs' 43 | }) 44 | }, 45 | onLoad: function () { 46 | if (app.globalData.userInfo) { 47 | this.setData({ 48 | userInfo: app.globalData.userInfo, 49 | hasUserInfo: true 50 | }) 51 | } else if (this.data.canIUse){ 52 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 53 | // 所以此处加入 callback 以防止这种情况 54 | app.userInfoReadyCallback = res => { 55 | this.setData({ 56 | userInfo: res.userInfo, 57 | hasUserInfo: true 58 | }) 59 | } 60 | } else { 61 | // 在没有 open-type=getUserInfo 版本的兼容处理 62 | wx.getUserInfo({ 63 | success: res => { 64 | app.globalData.userInfo = res.userInfo 65 | this.setData({ 66 | userInfo: res.userInfo, 67 | hasUserInfo: true 68 | }) 69 | } 70 | }) 71 | } 72 | }, 73 | getUserInfo: function(e) { 74 | console.log(e) 75 | app.globalData.userInfo = e.detail.userInfo 76 | this.setData({ 77 | userInfo: e.detail.userInfo, 78 | hasUserInfo: true 79 | }) 80 | } 81 | }) 82 | -------------------------------------------------------------------------------- /countDown/colorui/animation.wxss: -------------------------------------------------------------------------------- 1 | /* 2 | Animation 微动画 3 | 基于ColorUI组建库的动画模块 by 文晓港 2019年3月26日19:52:28 4 | */ 5 | 6 | /* css 滤镜 控制黑白底色gif的 */ 7 | .gif-black{ 8 | mix-blend-mode: screen; 9 | } 10 | .gif-white{ 11 | mix-blend-mode: multiply; 12 | } 13 | 14 | 15 | /* Animation css */ 16 | [class*=animation-] { 17 | animation-duration: .5s; 18 | animation-timing-function: ease-out; 19 | animation-fill-mode: both 20 | } 21 | 22 | .animation-fade { 23 | animation-name: fade; 24 | animation-duration: .8s; 25 | animation-timing-function: linear 26 | } 27 | 28 | .animation-scale-up { 29 | animation-name: scale-up 30 | } 31 | 32 | .animation-scale-down { 33 | animation-name: scale-down 34 | } 35 | 36 | .animation-slide-top { 37 | animation-name: slide-top 38 | } 39 | 40 | .animation-slide-bottom { 41 | animation-name: slide-bottom 42 | } 43 | 44 | .animation-slide-left { 45 | animation-name: slide-left 46 | } 47 | 48 | .animation-slide-right { 49 | animation-name: slide-right 50 | } 51 | 52 | .animation-shake { 53 | animation-name: shake 54 | } 55 | 56 | .animation-reverse { 57 | animation-direction: reverse 58 | } 59 | 60 | @keyframes fade { 61 | 0% { 62 | opacity: 0 63 | } 64 | 65 | 100% { 66 | opacity: 1 67 | } 68 | } 69 | 70 | @keyframes scale-up { 71 | 0% { 72 | opacity: 0; 73 | transform: scale(.2) 74 | } 75 | 76 | 100% { 77 | opacity: 1; 78 | transform: scale(1) 79 | } 80 | } 81 | 82 | @keyframes scale-down { 83 | 0% { 84 | opacity: 0; 85 | transform: scale(1.8) 86 | } 87 | 88 | 100% { 89 | opacity: 1; 90 | transform: scale(1) 91 | } 92 | } 93 | 94 | @keyframes slide-top { 95 | 0% { 96 | opacity: 0; 97 | transform: translateY(-100%) 98 | } 99 | 100 | 100% { 101 | opacity: 1; 102 | transform: translateY(0) 103 | } 104 | } 105 | 106 | @keyframes slide-bottom { 107 | 0% { 108 | opacity: 0; 109 | transform: translateY(100%) 110 | } 111 | 112 | 100% { 113 | opacity: 1; 114 | transform: translateY(0) 115 | } 116 | } 117 | 118 | @keyframes shake { 119 | 120 | 0%, 121 | 100% { 122 | transform: translateX(0) 123 | } 124 | 125 | 10% { 126 | transform: translateX(-9px) 127 | } 128 | 129 | 20% { 130 | transform: translateX(8px) 131 | } 132 | 133 | 30% { 134 | transform: translateX(-7px) 135 | } 136 | 137 | 40% { 138 | transform: translateX(6px) 139 | } 140 | 141 | 50% { 142 | transform: translateX(-5px) 143 | } 144 | 145 | 60% { 146 | transform: translateX(4px) 147 | } 148 | 149 | 70% { 150 | transform: translateX(-3px) 151 | } 152 | 153 | 80% { 154 | transform: translateX(2px) 155 | } 156 | 157 | 90% { 158 | transform: translateX(-1px) 159 | } 160 | } 161 | 162 | @keyframes slide-left { 163 | 0% { 164 | opacity: 0; 165 | transform: translateX(-100%) 166 | } 167 | 168 | 100% { 169 | opacity: 1; 170 | transform: translateX(0) 171 | } 172 | } 173 | 174 | @keyframes slide-right { 175 | 0% { 176 | opacity: 0; 177 | transform: translateX(100%) 178 | } 179 | 180 | 100% { 181 | opacity: 1; 182 | transform: translateX(0) 183 | } 184 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 写了几年代码,也赚不到啥钱。不如玩NFT。欢迎加群V:erwaNFT 2 | --- 3 | 4 | # 为什么要创建这个仓库?💪 5 | 6 | 7 | 👶小时候学习的经历告诉我,当学会了某个单一的知识点需要通过例题对它的概念以及用进行考察,以助于我能更好的掌握理解。 8 | 9 | 当一个章节学完后✍,还是通过例题对这个章节所涉及的知识点进行考察,从点到面的考察,将这些知识点贯穿起来,达到对知识点的更深入的理解。 10 | 11 | 相同地,我认为前端学习过程中,习题是至关重要的一部分,所以我收集了全网对应章节的笔试,面试题进行解答。📗 12 | 13 | 不定时更新面试题📚。为我自己加深知识点的同时,也希望能够给其他小伙伴带来便利。希望每个小伙伴都能学有所获,学以致用,然后能够取得自己心仪的offer👍 14 | 15 | ## 使用说明📑 16 | 17 | - 对相关知识点**概念**有初步了解👆 18 | - 根据题目思考解答思路✌ 19 | - 再点击进入查看题目答案👌 20 | 21 | ---- 22 | ## 目录 23 | 24 | ## 全网前端面试文章 25 |
26 | 掘金(点赞数过千的优秀文章) 27 | 28 | - [2020年前端面试复习必读文章](https://juejin.im/post/5e8b163ff265da47ee3f54a6) 29 | 30 | - [写给女朋友的中级前端面试秘籍(含详细答案,15k级别)](https://juejin.im/post/5e7af0685188255dcf4a497e) 31 | 32 | - [【前端面试分享】- 寒冬求职上篇](https://juejin.im/post/5cdb7bc26fb9a0321557044d) 33 | 34 | - [【前端面试分享】- 寒冬求职下篇](https://juejin.im/post/5cdcc009e51d453afb40d87c) 35 | 36 | - [中高级前端大厂面试秘籍,为你保驾护航金三银四,直通大厂(上)](https://juejin.im/post/5c64d15d6fb9a049d37f9c20) 37 | 38 | - [中高级前端大厂面试秘籍,寒冬中为您保驾护航,直通大厂(中篇)](https://juejin.im/post/5c92f499f265da612647b754) 39 | 40 | - [中高级前端大厂面试秘籍,寒冬中为您保驾护航,直通大厂(下篇)](https://juejin.im/post/5cc26dfef265da037b611738) 41 | 42 | - [web前端面试总结](https://juejin.im/post/5dafb263f265da5b9b80244d) 43 |
44 | 45 | ## 大厂技术分享 46 | 47 | - [腾讯前端 Web 团队IMWEB](http://imweb.io) 48 | - [腾讯 Web 前端团队 Alloy Team](http://www.alloyteam.com) 49 | - [淘宝前端团队](http://taobaofed.org) 50 | - [百度Web前端研发部](http://fex.baidu.com) 51 | - [京东凹凸实验室](https://aotu.io) 52 | - [360奇舞团](https://75team.com) 53 | - [七牛团队技术博客](http://blog.qiniu.com) 54 | - [有赞技术团队](http://tech.youzan.com) 55 | - [大搜车技术博客](https://blog.souche.com) 56 | - [美团](https://tech.meituan.com/) 57 | 58 | ## 前端在线书籍文档 59 |
60 | CSS/HTML 61 | 62 | * [学习CSS布局](http://zh.learnlayout.com/) 63 | * [通用 CSS 笔记、建议与指导](https://github.com/chadluo/CSS-Guidelines/blob/master/README.md) 64 | * [CSS参考手册](http://css.doyoe.com/) 65 | * [Emmet 文档](http://yanxyz.github.io/emmet-docs/) 66 | * [前端代码规范](http://alloyteam.github.io/CodeGuide/) (腾讯 AlloyTeam 团队) 67 | * [HTML和CSS编码规范](http://codeguide.bootcss.com/) 68 | * [Sass Guidelines 中文](http://sass-guidelin.es/zh/) 69 | * [CSS3 Tutorial 《CSS3 教程》](https://github.com/waylau/css3-tutorial) 70 | * [MDN HTML 中文文档](https://developer.mozilla.org/zh-CN/docs/Web/HTML) 71 | * [MDN CSS 中文文档](https://developer.mozilla.org/zh-CN/docs/Web/CSS) 72 |
73 |
74 | JavaScript 75 | 76 | * [现代 Javascript 教程](https://zh.javascript.info/) 77 | * [Google JavaScript 代码风格指南](http://bq69.com/blog/articles/script/868/google-javascript-style-guide.html) 78 | * [Google JSON 风格指南](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md) 79 | * [Airbnb JavaScript 规范](https://github.com/adamlu/javascript-style-guide) 80 | * [JavaScript 标准参考教程(alpha)](http://javascript.ruanyifeng.com/) 81 | * [Javascript编程指南](http://pij.robinqu.me/) ([源码](https://github.com/RobinQu/Programing-In-Javascript)) 82 | * [javascript 的 12 个怪癖](https://github.com/justjavac/12-javascript-quirks) 83 | * [JavaScript 秘密花园](http://bonsaiden.github.io/JavaScript-Garden/zh/) 84 | * [JavaScript核心概念及实践](http://icodeit.org/jsccp/) (PDF) (此书已由人民邮电出版社出版发行,但作者依然免费提供PDF版本,希望开发者们去购买,支持作者) 85 | * [《JavaScript 模式》](https://github.com/jayli/javascript-patterns) “JavaScript patterns”中译本 86 | * [命名函数表达式探秘](http://justjavac.com/named-function-expressions-demystified.html) (注:原文由[为之漫笔](http://www.cn-cuckoo.com)翻译,原始地址无法打开,所以此处地址为我博客上的备份) 87 | * [学用 JavaScript 设计模式](http://www.oschina.net/translate/learning-javascript-design-patterns) (开源中国) 88 | * [深入理解JavaScript系列](http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html) 89 | * [ECMAScript 5.1 中文版](http://yanhaijing.com/es5) 90 | * [ECMAScript 6 入门](http://es6.ruanyifeng.com/) (作者:阮一峰) 91 | * [JavaScript Promise迷你书](http://liubin.github.io/promises-book/) 92 | * [You-Dont-Know-JS](https://github.com/getify/You-Dont-Know-JS) (深入JavaScript语言核心机制的系列图书) 93 | * [JavaScript 教程](http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000) 廖雪峰 94 | * [MDN JavaScript 中文文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript) 95 | * jQuery 96 | * [jQuery 解构](http://www.cn-cuckoo.com/deconstructed/jquery.html) 97 | * [简单易懂的JQuery魔法](http://www.nowamagic.net/librarys/books/contents/jquery) 98 | * [How to write jQuery plugin](http://i5ting.github.io/How-to-write-jQuery-plugin/build/jquery.plugin.html) 99 | * [You Don't Need jQuery](https://github.com/oneuijs/You-Dont-Need-jQuery/blob/master/README.zh-CN.md) 100 | * [如何实现一个类jQuery?](https://github.com/MeCKodo/forchange) 101 | * underscore.js 102 | * [Underscore.js中文文档](http://learningcn.com/underscore/) 103 | * backbone.js 104 | * [backbone.js中文文档](http://www.css88.com/doc/backbone/) 105 | * [backbone.js入门教程](http://www.the5fire.com/backbone-js-tutorials-pdf-download.html) (PDF) 106 | * [Backbone.js入门教程第二版](https://github.com/the5fire/backbonejs-learning-note) 107 | * [Developing Backbone.js Applications(中文版)](http://feliving.github.io/developing-backbone-applications/) 108 | * AngularJS 109 | * [AngularJS最佳实践和风格指南](https://github.com/mgechev/angularjs-style-guide/blob/master/README-zh-cn.md) 110 | * [AngularJS中译本](https://github.com/peiransun/angularjs-cn) 111 | * [AngularJS入门教程](https://github.com/zensh/AngularjsTutorial_cn) 112 | * [构建自己的AngularJS](https://github.com/xufei/Make-Your-Own-AngularJS/blob/master/01.md) 113 | * [在Windows环境下用Yeoman构建AngularJS项目](http://www.waylau.com/build-angularjs-app-with-yeoman-in-windows/) 114 | * Zepto.js 115 | * [Zepto.js 中文文档](http://mweb.baidu.com/zeptoapi/) 116 | * Sea.js 117 | * [Hello Sea.js](http://island205.com/HelloSea.js/) 118 | * impress.js 119 | * [impress.js的中文教程](https://github.com/kokdemo/impress.js-tutorial-in-Chinese) 120 | * CoffeeScript 121 | * [CoffeeScript Cookbook](http://island205.com/coffeescript-cookbook.github.com/) 122 | * [The Little Book on CoffeeScript中文版](http://island205.com/tlboc/) 123 | * [CoffeeScript 编码风格指南](https://github.com/geekplux/coffeescript-style-guide) 124 | * TypeScipt 125 | * [TypeScript Handbook](https://zhongsp.gitbooks.io/typescript-handbook/content/) 126 | * ExtJS 127 | * [Ext4.1.0 中文文档](http://extjs-doc-cn.github.io/ext4api/) 128 | * Meteor 129 | * [Discover Meteor](http://zh.discovermeteor.com/) 130 | * [Meteor 中文文档](http://docs.meteorhub.org/#/basic/) 131 | * [Angular-Meteor 中文教程](http://angular.meteorhub.org/) 132 | * [Chrome扩展及应用开发](http://www.ituring.com.cn/minibook/950) 133 |
134 | 135 |
136 | React 137 | 138 | * [React 学习之道](https://github.com/the-road-to-learn-react/the-road-to-learn-react-chinese) 139 | * [React.js 小书](https://github.com/huzidaha/react-naive-book) 140 | * [React.js 中文文档](https://doc.react-china.org/) 141 | * [React webpack-cookbook](https://github.com/fakefish/react-webpack-cookbook) 142 | * [React 入门教程](http://fraserxu.me/intro-to-react/) 143 | * [React Native 中文文档(含最新Android内容)](http://wiki.jikexueyuan.com/project/react-native/) 144 | * [Learn React & Webpack by building the Hacker News front page](https://github.com/theJian/build-a-hn-front-page) 145 | 146 |
147 | 148 |
149 | NodeJS 150 | 151 | * [Node入门](http://www.nodebeginner.org/index-zh-cn.html) 152 | * [七天学会NodeJS](http://nqdeng.github.io/7-days-nodejs/) 153 | * [Nodejs Wiki Book](https://github.com/nodejs-tw/nodejs-wiki-book) (繁体中文) 154 | * [express.js 中文文档](http://expressjs.jser.us/) 155 | * [koa 中文文档](https://github.com/guo-yu/koa-guide) 156 | * [一起学koa](http://base-n.github.io/koa-generator-examples/) 157 | * [使用 Express + MongoDB 搭建多人博客](https://github.com/nswbmw/N-blog) 158 | * [Express框架](http://javascript.ruanyifeng.com/nodejs/express.html) 159 | * [Node.js 包教不包会](https://github.com/alsotang/node-lessons) 160 | * [Learn You The Node.js For Much Win! (中文版)](https://www.npmjs.com/package/learnyounode-zh-cn) 161 | * [Node debug 三法三例](http://i5ting.github.io/node-debug-tutorial/) 162 | * [nodejs中文文档](https://www.gitbook.com/book/0532/nodejs/details) 163 | * [orm2 中文文档](https://github.com/wizardforcel/orm2-doc-zh-cn) 164 | * [一起学 Node.js](https://github.com/nswbmw/N-blog) 165 | 166 |
167 | 168 | --- 169 | ## 前端学习(视频)网站推荐 170 | - [掘金](https://juejin.im/) 171 | - [思否](https://segmentfault.com/) 172 | - [慕课网](https://imooc.com) 173 | - [极客学院](http://www.jikexueyuan.com) 174 | - [前端进阶之道](https://yuchengkai.cn/docs/frontend/) 175 | - [牛客网](https://www.nowcoder.com/) 176 | 177 | ## 面试题 178 | - [JS概念类面试题汇总](https://github.com/hengxuZ/CSS-interview-question/tree/master/js/javascript-questions.md)👈 179 | - [HTML概念类面试题汇总](https://github.com/hengxuZ/CSS-interview-question/tree/master/html/html-questions.md)👈 180 | - [CSS概念类面试题汇总](https://github.com/hengxuZ/CSS-interview-question/tree/master/css/css-questions.md)👈 181 | - [前端知识思维导图](https://github.com/hengxuZ/CSS-interview-question/tree/master/javascript思维导图.md) 182 | -------------------------------------------------------------------------------- /html/html-questions.md: -------------------------------------------------------------------------------- 1 | # HTML 问题 2 | 3 | 欢迎提出 PR 进行建议和指正! 4 | 5 | * [`DOCTYPE`有什么用?](#doctype有什么用) 6 | * [如何提供包含多种语言内容的页面?](#如何提供包含多种语言内容的页面) 7 | * [在设计开发多语言网站时,需要留心哪些事情?](#在设计开发多语言网站时需要留心哪些事情) 8 | * [什么是`data-`属性?](#什么是data-属性) 9 | * [将 HTML5 看作成开放的网络平台,什么是 HTML5 的基本构件(building block)?](#将-html5-看作成开放的网络平台什么是-html5-的基本构件building-block) 10 | * [请描述`cookie`、`sessionStorage`和`localStorage`的区别。](#请描述cookiesessionstorage和localstorage的区别) 11 | * [请描述` 814 | ``` 815 | 816 | - IE5-8不支持``opacity``,解决办法: 817 | 818 | ``` 819 | .opacity { 820 | opacity: 0.4 821 | filter: alpha(opacity=60); /* for IE5-7 */ 822 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; /* for IE 8*/ 823 | } 824 | ``` 825 | 826 | - IE6在设置``height``小于``font-size``时高度值为``font-size``,解决办法:``font-size: 0;`` 827 | - IE6不支持PNG透明背景,解决办法: **IE6下使用gif图片** 828 | - IE6-7不支持``display: inline-block``解决办法:设置inline并触发hasLayout 829 | 830 | ``` 831 | display: inline-block; 832 | *display: inline; 833 | *zoom: 1; 834 | ``` 835 | 836 | - IE6下浮动元素在浮动方向上与父元素边界接触元素的外边距会加倍。解决办法: 837 | 1)使用padding控制间距。 838 | 2)浮动元素``display: inline;``这样解决问题且无任何副作用:css标准规定浮动元素display:inline会自动调整为block 839 | - 通过为块级元素设置宽度和左右margin为auto时,IE6不能实现水平居中,解决方法:为父元素设置``text-align: center;`` 840 | 841 | ### 容器包含若干浮动元素时如何清理(包含)浮动 842 | 843 | 1. 容器元素闭合标签前添加额外元素并设置``clear: both`` 844 | 2. 父元素触发块级格式化上下文(见块级可视化上下文部分) 845 | 3. 设置容器元素伪元素进行清理[推荐的清理浮动方法](http://nicolasgallagher.com/micro-clearfix-hack/) 846 | 847 | ``` 848 | /** 849 | * 在标准浏览器下使用 850 | * 1 content内容为空格用于修复opera下文档中出现 851 | * contenteditable属性时在清理浮动元素上下的空白 852 | * 2 使用display使用table而不是block:可以防止容器和 853 | * 子元素top-margin折叠,这样能使清理效果与BFC,IE6/7 854 | * zoom: 1;一致 855 | **/ 856 | 857 | .clearfix:before, 858 | .clearfix:after { 859 | content: " "; /* 1 */ 860 | display: table; /* 2 */ 861 | } 862 | 863 | .clearfix:after { 864 | clear: both; 865 | } 866 | 867 | /** 868 | * IE 6/7下使用 869 | * 通过触发hasLayout实现包含浮动 870 | **/ 871 | .clearfix { 872 | *zoom: 1; 873 | } 874 | ``` 875 | 876 | ### 什么是FOUC?如何避免 877 | Flash Of Unstyled Content:用户定义样式表加载之前浏览器使用默认样式显示文档,用户样式加载渲染之后再从新显示文档,造成页面闪烁。**解决方法**:把样式表放到文档的`head` 878 | 879 | ### 如何创建块级格式化上下文(block formatting context),BFC有什么用 880 | 创建规则: 881 | 882 | 1. 根元素 883 | 2. 浮动元素(``float``不是``none``) 884 | 3. 绝对定位元素(``position``取值为``absolute``或``fixed``) 885 | 4. ``display``取值为``inline-block``,``table-cell``, ``table-caption``,``flex``, ``inline-flex``之一的元素 886 | 5. ``overflow``不是``visible``的元素 887 | 888 | 889 | 作用: 890 | 891 | 1. 可以包含浮动元素 892 | 2. 不被浮动元素覆盖 893 | 3. 阻止父子元素的margin折叠 894 | 895 | ### display,float,position的关系 896 | 897 | 1. 如果``display``为none,那么position和float都不起作用,这种情况下元素不产生框 898 | 2. 否则,如果position值为absolute或者fixed,框就是绝对定位的,float的计算值为none,display根据下面的表格进行调整。 899 | 3. 否则,如果float不是none,框是浮动的,display根据下表进行调整 900 | 4. 否则,如果元素是根元素,display根据下表进行调整 901 | 5. 其他情况下display的值为指定值 902 | 总结起来:**绝对定位、浮动、根元素都需要调整``display``** 903 | ![display转换规则](/img/display-adjust.png) 904 | 905 | ### 外边距折叠(collapsing margins) 906 | 毗邻的两个或多个``margin``会合并成一个margin,叫做外边距折叠。规则如下: 907 | 908 | 1. 两个或多个毗邻的普通流中的块元素垂直方向上的margin会折叠 909 | 2. 浮动元素/inline-block元素/绝对定位元素的margin不会和垂直方向上的其他元素的margin折叠 910 | 3. 创建了块级格式化上下文的元素,不会和它的子元素发生margin折叠 911 | 4. 元素自身的margin-bottom和margin-top相邻时也会折叠 912 | 913 | ### 如何确定一个元素的包含块(containing block) 914 | 915 | 1. 根元素的包含块叫做初始包含块,在连续媒体中他的尺寸与viewport相同并且anchored at the canvas origin;对于paged media,它的尺寸等于page area。初始包含块的direction属性与根元素相同。 916 | 2. ``position``为``relative``或者``static``的元素,它的包含块由最近的块级(``display``为``block``,``list-item``, ``table``)祖先元素的**内容框**组成 917 | 3. 如果元素``position``为``fixed``。对于连续媒体,它的包含块为viewport;对于paged media,包含块为page area 918 | 4. 如果元素``position``为``absolute``,它的包含块由祖先元素中最近一个``position``为``relative``,``absolute``或者``fixed``的元素产生,规则如下: 919 | - 如果祖先元素为行内元素,the containing block is the bounding box around the **padding boxes** of the first and the last inline boxes generated for that element. 920 | - 其他情况下包含块由祖先节点的**padding edge**组成 921 | 922 | 如果找不到定位的祖先元素,包含块为**初始包含块** 923 | 924 | ### stacking context,布局规则 925 | z轴上的默认层叠顺序如下(从下到上): 926 | 927 | 1. 根元素的边界和背景 928 | 2. 常规流中的元素按照html中顺序 929 | 3. 浮动块 930 | 4. positioned元素按照html中出现顺序 931 | 932 | 如何创建stacking context: 933 | 934 | 1. 根元素 935 | 2. z-index不为auto的定位元素 936 | 3. a flex item with a z-index value other than 'auto' 937 | 4. opacity小于1的元素 938 | 5. 在移动端webkit和chrome22+,z-index为auto,position: fixed也将创建新的stacking context 939 | 940 | ### 如何水平居中一个元素 941 | - 如果需要居中的元素为**常规流中inline元素**,为父元素设置`text-align: center;`即可实现 942 | - 如果需要居中的元素为**常规流中block元素**,1)为元素设置宽度,2)设置左右margin为auto。3)IE6下需在父元素上设置`text-align: center;`,再给子元素恢复需要的值 943 | 944 | ``` 945 | 946 |
947 | aaaaaa aaaaaa a a a a a a a a 948 |
949 | 950 | 951 | 964 | ``` 965 | 966 | - 如果需要居中的元素为**浮动元素**,1)为元素设置宽度,2)`position: relative;`,3)浮动方向偏移量(left或者right)设置为50%,4)浮动方向上的margin设置为元素宽度一半乘以-1 967 | 968 | ``` 969 | 970 |
971 | aaaaaa aaaaaa a a a a a a a a 972 |
973 | 974 | 975 | 990 | ``` 991 | 992 | - 如果需要居中的元素为**绝对定位元素**,1)为元素设置宽度,2)偏移量设置为50%,3)偏移方向外边距设置为元素宽度一半乘以-1 993 | 994 | ``` 995 | 996 |
997 | aaaaaa aaaaaa a a a a a a a a 998 |
999 | 1000 | 1001 | 1016 | ``` 1017 | 1018 | - 如果需要居中的元素为**绝对定位元素**,1)为元素设置宽度,2)设置左右偏移量都为0,3)设置左右外边距都为auto 1019 | 1020 | ``` 1021 | 1022 |
1023 | aaaaaa aaaaaa a a a a a a a a 1024 |
1025 | 1026 | 1027 | 1043 | ``` 1044 | 1045 | ### 如何竖直居中一个元素 1046 | 参考资料:[6 Methods For Vertical Centering With CSS](http://www.vanseodesign.com/css/vertical-centering/)。 [盘点8种CSS实现垂直居中](http://blog.csdn.net/freshlover/article/details/11579669) 1047 | 1048 | - 需要居中元素为**单行文本**,为包含文本的元素设置大于`font-size`的`line-height`: 1049 | 1050 | ``` 1051 |

center text

1052 | 1053 | 1058 | ``` 1059 | 1060 | -------------------------------------------------------------------------------- /js/javascript-questions.md: -------------------------------------------------------------------------------- 1 | ## JS 问题 2 | 3 | 欢迎提出 PR 进行建议和指正! 4 | 5 | * [请解释事件委托(event delegation)。](#请解释事件委托event-delegation) 6 | * [请简述`JavaScript`中的`this`。](#请简述javascript中的this) 7 | * [请解释原型继承(prototypal inheritance)的工作原理。](#请解释原型继承prototypal-inheritance的工作原理) 8 | * [说说你对 AMD 和 CommonJS 的了解。](#说说你对-amd-和-commonjs-的了解) 9 | * [请解释下面代码为什么不能用作 IIFE:`function foo(){ }();`,需要作出哪些修改才能使其成为 IIFE?](#请解释下面代码为什么不能用作-iifefunction-foo-需要作出哪些修改才能使其成为-iife) 10 | * [`null`、`undefined`和未声明变量之间有什么区别?如何检查判断这些状态值?](#nullundefined和未声明变量之间有什么区别如何检查判断这些状态值) 11 | * [什么是闭包(closure),为什么使用闭包?](#什么是闭包closure为什么使用闭包) 12 | * [请说明`.forEach`循环和`.map()`循环的主要区别,它们分别在什么情况下使用?](#请说明foreach循环和map循环的主要区别它们分别在什么情况下使用) 13 | * [匿名函数的典型应用场景是什么?](#匿名函数的典型应用场景是什么) 14 | * [你如何组织自己的代码?(使用模块模式(module pattern)还是经典继承(classical inheritance)?)](#你如何组织自己的代码使用模块模式module-pattern还是经典继承classical-inheritance) 15 | * [宿主对象(host objects)和原生对象(native objects)的区别是什么?](#宿主对象host-objects和原生对象native-objects的区别是什么) 16 | * [下列语句有什么区别:`function Person(){}`、`var person = Person()`和`var person = new Person()`?](#下列语句有什么区别function-personvar-person--person和var-person--new-person) 17 | * [`.call`和`.apply`有什么区别?](#call和apply有什么区别) 18 | * [请说明`Function.prototype.bind`的用法。](#请说明functionprototypebind的用法) 19 | * [什么时候会用到`document.write()`?](#什么时候会用到documentwrite) 20 | * [功能检测(feature detection)、功能推断(feature inference)和使用 UA 字符串之间有什么区别?](#功能检测feature-detection功能推断feature-inference和使用-ua-字符串之间有什么区别) 21 | * [请尽可能详细地解释 Ajax。](#请尽可能详细地解释-ajax) 22 | * [使用 Ajax 的优缺点分别是什么?](#使用ajax的优缺点分别是什么) 23 | * [请说明 JSONP 的工作原理,它为什么不是真正的 Ajax?](#请说明-jsonp-的工作原理它为什么不是真正的-ajax) 24 | * [你使用过 JavaScript 模板吗?用过什么相关的库?](#你使用过-javascript-模板吗用过什么相关的库) 25 | * [请解释变量提升(hoisting)。](#请解释变量提升hoisting) 26 | * [请描述事件冒泡。](#请描述事件冒泡) 27 | * [“attribute” 和 “property” 之间有什么区别?](#attribute-和-property-之间有什么区别) 28 | * [为什么扩展 JavaScript 内置对象是不好的做法?](#为什么扩展-javascript-内置对象是不好的做法) 29 | * [document 中的`load`事件和`DOMContentLoaded`事件之间的区别是什么?](#document-中的load事件和domcontentloaded事件之间的区别是什么) 30 | * [`==`和`===`的区别是什么?](#和的区别是什么) 31 | * [请解释关于 JavaScript 的同源策略。](#请解释关于-javascript-的同源策略) 32 | * [请使下面的语句生效:](#请使下面的语句生效) 33 | 34 | ```js 35 | duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5] 36 | ``` 37 | 38 | * [请说明三元表达式中“三元”这个词代表什么?](#请说明三元表达式中三元这个词代表什么) 39 | * [什么是`"use strict";`?使用它有什么优缺点?](#什么是use-strict使用它有什么优缺点) 40 | * [创建一个循环,从 1 迭代到 100,`3`的倍数时输出 "fizz",`5`的倍数时输出 "buzz",同时为`3`和`5`的倍数时输出 "fizzbuzz"。](#创建一个循环从1迭代到1003的倍数时输出-fizz5的倍数时输出-buzz同时为3和5的倍数时输出-fizzbuzz) 41 | * [为什么不要使用全局作用域?](#为什么不要使用全局作用域) 42 | * [为什么要使用`load`事件?这个事件有什么缺点吗?你知道一些代替方案吗,为什么使用它们?](#为什么要使用load事件这个事件有什么缺点吗你知道一些代替方案吗为什么使用它们) 43 | * [请解释单页应用是什么,如何使其对 SEO 友好。](#请解释单页应用是什么如何使其对seo友好) 44 | * [你对 Promises 及其 polyfill 的掌握程度如何?](#你对-promises-及其-polyfill-的掌握程度如何) 45 | * [`Promise`代替回调函数有什么优缺点?](#promise代替回调函数有什么优缺点) 46 | * [用转译成 JavaScript 的语言写 JavaScript 有什么优缺点?](#用转译成-javascript-的语言写-javascript-有什么优缺点) 47 | * [你使用什么工具和技巧调试 JavaScript 代码?](#你使用什么工具和技巧调试-javascript-代码) 48 | * [你使用什么语句遍历对象的属性和数组的元素?](#你使用什么语句遍历对象的属性和数组的元素) 49 | * [请解释可变对象和不可变对象之间的区别。](#请解释可变对象和不可变对象之间的区别) 50 | * [请解释同步和异步函数之间的区别。](#请解释同步和异步函数之间的区别) 51 | * [什么是事件循环?调用堆栈和任务队列之间有什么区别?](#什么是事件循环调用堆栈和任务队列之间有什么区别) 52 | * [请解释`function foo() {}`和`var foo = function() {}`之间`foo`的用法上的区别。](#请解释function-foo-和var-foo--function-之间foo的用法上的区别) 53 | * [使用`let`、`var`和`const`创建变量有什么区别?](#使用letvar和const创建变量有什么区别) 54 | * [ES6 的类和 ES5 的构造函数有什么区别?](#es6-的类和-es5-的构造函数有什么区别) 55 | * [你能给出一个使用箭头函数的例子吗,箭头函数与其他函数有什么不同?](#你能给出一个使用箭头函数的例子吗箭头函数与其他函数有什么不同) 56 | * [在构造函数中使用箭头函数有什么好处?](#在构造函数中使用箭头函数有什么好处) 57 | * [高阶函数(higher-order)的定义是什么?](#高阶函数higher-order的定义是什么) 58 | * [请给出一个解构(destructuring)对象或数组的例子。](#请给出一个解构destructuring对象或数组的例子) 59 | * [ES6 的模板字符串为生成字符串提供了很大的灵活性,你可以举个例子吗?](#es6-的模板字符串为生成字符串提供了很大的灵活性你可以举个例子吗) 60 | * [你能举出一个柯里化函数(curry function)的例子吗?它有哪些好处?](#你能举出一个柯里化函数curry-function的例子吗它有哪些好处) 61 | * [使用扩展运算符(spread)的好处是什么,它与使用剩余参数语句(rest)有什么区别?](#使用扩展运算符spread的好处是什么它与使用剩余参数语句rest有什么区别) 62 | * [如何在文件之间共用代码?](#如何在文件之间共用代码) 63 | * [什么情况下会用到静态类成员?](#什么情况下会用到静态类成员) 64 | * 65 | - [请用原生js实现一个函数,给页面制定的任意一个元素添加一个透明遮罩(透明度可变,默认0.2),使这个区域点击无效,要求兼容IE8+及各主流浏览器,遮罩层效果如下图所示:](#请用原生js实现一个函数给页面制定的任意一个元素添加一个透明遮罩透明度可变默认02使这个区域点击无效要求兼容ie8及各主流浏览器遮罩层效果如下图所示) 66 | - [请用代码写出(今天是星期x)其中x表示当天是星期几,如果当天是星期一,输出应该是"今天是星期一"](#请用代码写出今天是星期x其中x表示当天是星期几如果当天是星期一输出应该是今天是星期一) 67 | - [下面这段代码想要循环延时输出结果0 1 2 3 4,请问输出结果是否正确,如果不正确,请说明为什么,并修改循环内的代码使其输出正确结果](#下面这段代码想要循环延时输出结果0-1-2-3-4请问输出结果是否正确如果不正确请说明为什么并修改循环内的代码使其输出正确结果) 68 | - [现有一个Page类,其原型对象上有许多以post开头的方法(如postMsg);另有一拦截函数chekc,只返回ture或false.请设计一个函数,该函数应批量改造原Page的postXXX方法,在保留其原有功能的同时,为每个postXXX方法增加拦截验证功能,当chekc返回true时继续执行原postXXX方法,返回false时不再执行原postXXX方法](#现有一个page类其原型对象上有许多以post开头的方法如postmsg另有一拦截函数chekc只返回ture或false请设计一个函数该函数应批量改造原page的postxxx方法在保留其原有功能的同时为每个postxxx方法增加拦截验证功能当chekc返回true时继续执行原postxxx方法返回false时不再执行原postxxx方法) 69 | - [完成下面的tool-tip](#完成下面的tool-tip) 70 | - [编写javascript深度克隆函数deepClone](#编写javascript深度克隆函数deepclone) 71 | - [补充代码,鼠标单击Button1后将Button1移动到Button2的后面](#补充代码鼠标单击button1后将button1移动到button2的后面) 72 | - [网页中实现一个计算当年还剩多少时间的倒数计时程序,要求网页上实时动态显示"××年还剩××天××时××分××秒"](#网页中实现一个计算当年还剩多少时间的倒数计时程序要求网页上实时动态显示××年还剩××天××时××分××秒) 73 | - [完成一个函数,接受数组作为参数,数组元素为整数或者数组,数组元素包含整数或数组,函数返回扁平化后的数组](#完成一个函数接受数组作为参数数组元素为整数或者数组数组元素包含整数或数组函数返回扁平化后的数组) 74 | - [如何判断一个对象是否为数组](#如何判断一个对象是否为数组) 75 | - [请评价以下事件监听器代码并给出改进意见](#请评价以下事件监听器代码并给出改进意见) 76 | - [如何判断一个对象是否为函数](#如何判断一个对象是否为函数) 77 | - [编写一个函数接受url中query string为参数,返回解析后的Object,query string使用application/x-www-form-urlencoded编码](#编写一个函数接受url中query-string为参数返回解析后的objectquery-string使用applicationx-www-form-urlencoded编码) 78 | - [解析一个完整的url,返回Object包含域与window.location相同](#解析一个完整的url返回object包含域与windowlocation相同) 79 | - [完成函数getViewportSize返回指定窗口的视口尺寸](#完成函数getviewportsize返回指定窗口的视口尺寸) 80 | - [完成函数getScrollOffset返回窗口滚动条偏移量](#完成函数getscrolloffset返回窗口滚动条偏移量) 81 | - [现有一个字符串richText,是一段富文本,需要显示在页面上.有个要求,需要给其中只包含一个img元素的p标签增加一个叫pic的class.请编写代码实现.可以使用jQuery或KISSY.](#现有一个字符串richtext是一段富文本需要显示在页面上有个要求需要给其中只包含一个img元素的p标签增加一个叫pic的class请编写代码实现可以使用jquery或kissy) 82 | - [请实现一个Event类,继承自此类的对象都会拥有两个方法on,off,once和trigger](#请实现一个event类继承自此类的对象都会拥有两个方法onoffonce和trigger) 83 | - [编写一个函数将列表子元素顺序反转](#编写一个函数将列表子元素顺序反转) 84 | - [以下函数的作用是?空白区域应该填写什么](#以下函数的作用是空白区域应该填写什么) 85 | - [编写一个函数实现form的序列化(即将一个表单中的键值序列化为可提交的字符串)](#编写一个函数实现form的序列化即将一个表单中的键值序列化为可提交的字符串) 86 | - [使用原生javascript给下面列表中的li节点绑定点击事件,点击时创建一个Object对象,兼容IE和标准浏览器](#使用原生javascript给下面列表中的li节点绑定点击事件点击时创建一个object对象兼容ie和标准浏览器) 87 | - [有一个大数组,var a = ['1', '2', '3', ...];a的长度是100,内容填充随机整数的字符串.请先构造此数组a,然后设计一个算法将其内容去重](#有一个大数组var-a-=-1-2-3-a的长度是100内容填充随机整数的字符串请先构造此数组a然后设计一个算法将其内容去重) 88 | ### 请解释事件委托(event delegation)。 89 | 90 | 事件委托是将事件监听器添加到父元素,而不是每个子元素单独设置事件监听器。当触发子元素时,事件会冒泡到父元素,监听器就会触发。这种技术的好处是: 91 | 92 | * 内存占用减少,因为只需要一个父元素的事件处理程序,而不必为每个后代都添加事件处理程序。 93 | * 无需从已删除的元素中解绑处理程序,也无需将处理程序绑定到新元素上。 94 | 95 | ###### 参考 96 | 97 | * https://davidwalsh.name/event-delegate 98 | * https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation 99 | 100 | [[↑] 回到顶部](#js-问题) 101 | 102 | ### 请简述`JavaScript`中的`this`。 103 | 104 | JS 中的`this`是一个相对复杂的概念,不是简单几句能解释清楚的。粗略地讲,函数的调用方式决定了`this`的值。我阅读了网上很多关于`this`的文章,[Arnav Aggrawal](https://medium.com/@arnav_aggarwal) 写的比较清楚。`this`取值符合以下规则: 105 | 106 | 1. 在调用函数时使用`new`关键字,函数内的`this`是一个全新的对象。 107 | 2. 如果`apply`、`call`或`bind`方法用于调用、创建一个函数,函数内的 this 就是作为参数传入这些方法的对象。 108 | 3. 当函数作为对象里的方法被调用时,函数内的`this`是调用该函数的对象。比如当`obj.method()`被调用时,函数内的 this 将绑定到`obj`对象。 109 | 4. 如果调用函数不符合上述规则,那么`this`的值指向全局对象(global object)。浏览器环境下`this`的值指向`window`对象,但是在严格模式下(`'use strict'`),`this`的值为`undefined`。 110 | 5. 如果符合上述多个规则,则较高的规则(1 号最高,4 号最低)将决定`this`的值。 111 | 6. 如果该函数是 ES2015 中的箭头函数,将忽略上面的所有规则,`this`被设置为它被创建时的上下文。 112 | 113 | 想获得更深入的解释,请查看[他在 Medium 上的文章](https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3)。 114 | 115 | ###### 参考 116 | 117 | * https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3 118 | * https://stackoverflow.com/a/3127440/1751946 119 | 120 | [[↑] 回到顶部](#js-问题) 121 | 122 | ### 请解释原型继承(prototypal inheritance)的工作原理。 123 | 124 | 这是一个非常常见的 JavaScript 问题。所有 JS 对象都有一个`prototype`属性,指向它的原型对象。当试图访问一个对象的属性时,如果没有在该对象上找到,它还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。这种行为是在模拟经典的继承,[但是与其说是继承,不如说是委托(delegation)](https://davidwalsh.name/javascript-objects)。 125 | 126 | ###### 参考 127 | 128 | * https://www.quora.com/What-is-prototypal-inheritance/answer/Kyle-Simpson 129 | * https://davidwalsh.name/javascript-objects 130 | 131 | [[↑] 回到顶部](#js-问题) 132 | 133 | ### 说说你对 AMD 和 CommonJS 的了解。 134 | 135 | 它们都是实现模块体系的方式,直到 ES2015 出现之前,JavaScript 一直没有模块体系。CommonJS 是同步的,而 AMD(Asynchronous Module Definition)从全称中可以明显看出是异步的。CommonJS 的设计是为服务器端开发考虑的,而 AMD 支持异步加载模块,更适合浏览器。 136 | 137 | 我发现 AMD 的语法非常冗长,CommonJS 更接近其他语言 import 声明语句的用法习惯。大多数情况下,我认为 AMD 没有使用的必要,因为如果把所有 JavaScript 都捆绑进一个文件中,将无法得到异步加载的好处。此外,CommonJS 语法上更接近 Node 编写模块的风格,在前后端都使用 JavaScript 开发之间进行切换时,语境的切换开销较小。 138 | 139 | 我很高兴看到 ES2015 的模块加载方案同时支持同步和异步,我们终于可以只使用一种方案了。虽然它尚未在浏览器和 Node 中完全推出,但是我们可以使用代码转换工具进行转换。 140 | 141 | ###### 参考 142 | 143 | * https://auth0.com/blog/javascript-module-systems-showdown/ 144 | * https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs 145 | 146 | [[↑] 回到顶部](#js-问题) 147 | 148 | ### 请解释下面代码为什么不能用作 IIFE:`function foo(){ }();`,需要作出哪些修改才能使其成为 IIFE? 149 | 150 | IIFE(Immediately Invoked Function Expressions)代表立即执行函数。 JavaScript 解析器将 `function foo(){ }();`解析成`function foo(){ }`和`();`。其中,前者是函数声明;后者(一对括号)是试图调用一个函数,却没有指定名称,因此它会抛出`Uncaught SyntaxError: Unexpected token )`的错误。 151 | 152 | 修改方法是:再添加一对括号,形式上有两种:`(function foo(){ })()`和`(function foo(){ }())`。以上函数不会暴露到全局作用域,如果不需要在函数内部引用自身,可以省略函数的名称。 153 | 154 | 你可能会用到 `void` 操作符:`void function foo(){ }();`。但是,这种做法是有问题的。表达式的值是`undefined`,所以如果你的 IIFE 有返回值,不要用这种做法。例如: 155 | 156 | ``` 157 | // Don't add JS syntax to this code block to prevent Prettier from formatting it. 158 | const foo = void function bar() { return 'foo'; }(); 159 | 160 | console.log(foo); // undefined 161 | ``` 162 | 163 | ###### 参考 164 | 165 | * http://lucybain.com/blog/2014/immediately-invoked-function-expression/ 166 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void 167 | 168 | [[↑] 回到顶部](#js-问题) 169 | 170 | ### `null`、`undefined`和未声明变量之间有什么区别?如何检查判断这些状态值? 171 | 172 | 当你没有提前使用`var`、`let`或`const`声明变量,就为一个变量赋值时,该变量是未声明变量(undeclared variables)。未声明变量会脱离当前作用域,成为全局作用域下定义的变量。在严格模式下,给未声明的变量赋值,会抛出`ReferenceError`错误。和使用全局变量一样,使用未声明变量也是非常不好的做法,应当尽可能避免。要检查判断它们,需要将用到它们的代码放在`try`/`catch`语句中。 173 | 174 | ```js 175 | function foo() { 176 | x = 1; // 在严格模式下,抛出 ReferenceError 错误 177 | } 178 | 179 | foo(); 180 | console.log(x); // 1 181 | ``` 182 | 183 | 当一个变量已经声明,但没有赋值时,该变量的值是`undefined`。如果一个函数的执行结果被赋值给一个变量,但是这个函数却没有返回任何值,那么该变量的值是`undefined`。要检查它,需要使用严格相等(`===`);或者使用`typeof`,它会返回`'undefined'`字符串。请注意,不能使用非严格相等(`==`)来检查,因为如果变量值为`null`,使用非严格相等也会返回`true`。 184 | 185 | ```js 186 | var foo; 187 | console.log(foo); // undefined 188 | console.log(foo === undefined); // true 189 | console.log(typeof foo === 'undefined'); // true 190 | 191 | console.log(foo == null); // true. 错误,不要使用非严格相等! 192 | 193 | function bar() {} 194 | var baz = bar(); 195 | console.log(baz); // undefined 196 | ``` 197 | 198 | `null`只能被显式赋值给变量。它表示`空值`,与被显式赋值 `undefined` 的意义不同。要检查判断`null`值,需要使用严格相等运算符。请注意,和前面一样,不能使用非严格相等(`==`)来检查,因为如果变量值为`undefined`,使用非严格相等也会返回`true`。 199 | 200 | ```js 201 | var foo = null; 202 | console.log(foo === null); // true 203 | 204 | console.log(foo == undefined); // true. 错误,不要使用非严格相等! 205 | ``` 206 | 207 | 作为一种个人习惯,我从不使用未声明变量。如果定义了暂时没有用到的变量,我会在声明后明确地给它们赋值为`null`。 208 | 209 | ###### 参考 210 | 211 | * https://stackoverflow.com/questions/15985875/effect-of-declared-and-undeclared-variables 212 | * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined 213 | 214 | [[↑] 回到顶部](#js-问题) 215 | 216 | ### 什么是闭包(closure),为什么使用闭包? 217 | 218 | 闭包是函数和声明该函数的词法环境的组合。词法作用域中使用的域,是变量在代码中声明的位置所决定的。闭包是即使被外部函数返回,依然可以访问到外部(封闭)函数作用域的函数。 219 | 220 | **为什么使用闭包?** 221 | 222 | * 利用闭包实现数据私有化或模拟私有方法。这个方式也称为[模块模式(module pattern)](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript)。 223 | * [部分参数函数(partial applications)柯里化(currying)](https://medium.com/javascript-scene/curry-or-partial-application-8150044c78b8#.l4b6l1i3x). 224 | 225 | ###### 参考 226 | 227 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures 228 | * https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36 229 | 230 | [[↑] 回到顶部](#js-问题) 231 | 232 | ### 请说明`.forEach`循环和`.map()`循环的主要区别,它们分别在什么情况下使用? 233 | 234 | 为了理解两者的区别,我们看看它们分别是做什么的。 235 | 236 | **`forEach`** 237 | 238 | * 遍历数组中的元素。 239 | * 为每个元素执行回调。 240 | * 无返回值。 241 | 242 | ```js 243 | const a = [1, 2, 3]; 244 | const doubled = a.forEach((num, index) => { 245 | // 执行与 num、index 相关的代码 246 | }); 247 | 248 | // doubled = undefined 249 | ``` 250 | 251 | **`map`** 252 | 253 | * 遍历数组中的元素 254 | * 通过对每个元素调用函数,将每个元素“映射(map)”到一个新元素,从而创建一个新数组。 255 | 256 | ```js 257 | const a = [1, 2, 3]; 258 | const doubled = a.map(num => { 259 | return num * 2; 260 | }); 261 | 262 | // doubled = [2, 4, 6] 263 | ``` 264 | 265 | `.forEach`和`.map()`的主要区别在于`.map()`返回一个新的数组。如果你想得到一个结果,但不想改变原始数组,用`.map()`。如果你只需要在数组上做迭代修改,用`forEach`。 266 | 267 | ###### 参考 268 | 269 | * https://codeburst.io/javascript-map-vs-foreach-f38111822c0f 270 | 271 | [[↑] 回到顶部](#js-问题) 272 | 273 | ### 匿名函数的典型应用场景是什么? 274 | 275 | 匿名函数可以在 IIFE 中使用,来封装局部作用域内的代码,以便其声明的变量不会暴露到全局作用域。 276 | 277 | ```js 278 | (function() { 279 | // 一些代码。 280 | })(); 281 | ``` 282 | 283 | 匿名函数可以作为只用一次,不需要在其他地方使用的回调函数。当处理函数在调用它们的程序内部被定义时,代码具有更好地自闭性和可读性,可以省去寻找该处理函数的函数体位置的麻烦。 284 | 285 | ```js 286 | setTimeout(function() { 287 | console.log('Hello world!'); 288 | }, 1000); 289 | ``` 290 | 291 | 匿名函数可以用于函数式编程或 Lodash(类似于回调函数)。 292 | 293 | ```js 294 | const arr = [1, 2, 3]; 295 | const double = arr.map(function(el) { 296 | return el * 2; 297 | }); 298 | console.log(double); // [2, 4, 6] 299 | ``` 300 | 301 | ###### 参考 302 | 303 | * https://www.quora.com/What-is-a-typical-usecase-for-anonymous-functions 304 | * https://stackoverflow.com/questions/10273185/what-are-the-benefits-to-using-anonymous-functions-instead-of-named-functions-fo 305 | 306 | [[↑] 回到顶部](#js-问题) 307 | 308 | ### 你如何组织自己的代码?(使用模块模式(module pattern)还是经典继承(classical inheritance)?) 309 | 310 | 我以前使用 Backbone 组织我的模型(model),Backbone 鼓励采用面向对象的方法——创建 Backbone 模型,并为其添加方法。 311 | 312 | 模块模式仍然是很好的方式,但是现在我使用基于 React/Redux 的 Flux 体系结构,它鼓励使用单向函数编程的方法。我用普通对象(plain object)表示我的 app 模型,编写实用纯函数去操作这些对象。使用动作(actions)和化简器(reducers)来处理状态,就像其他 Redux 应用一样。 313 | 314 | 我尽可能避免使用经典继承。如果非要这么做,我会坚持[这些原则](https://medium.com/@dan_abramov/how-to-use-classes-and-sleep-at-night-9af8de78ccb4)。 315 | 316 | [[↑] 回到顶部](#js-问题) 317 | 318 | ### 宿主对象(host objects)和原生对象(native objects)的区别是什么? 319 | 320 | 原生对象是由 ECMAScript 规范定义的 JavaScript 内置对象,比如`String`、`Math`、`RegExp`、`Object`、`Function`等等。 321 | 322 | 宿主对象是由运行时环境(浏览器或 Node)提供,比如`window`、`XMLHTTPRequest`等等。 323 | 324 | ###### 参考 325 | 326 | * https://stackoverflow.com/questions/7614317/what-is-the-difference-between-native-objects-and-host-objects 327 | 328 | [[↑] 回到顶部](#js-问题) 329 | 330 | ### 下列语句有什么区别:`function Person(){}`、`var person = Person()`和`var person = new Person()`? 331 | 332 | 这个问题问得很含糊。我猜这是在考察 JavaScript 中的构造函数(constructor)。从技术上讲,`function Person(){}`只是一个普通的函数声明。使用 PascalCase 方式命名函数作为构造函数,是一个惯例。 333 | 334 | `var person = Person()`将`Person`以普通函数调用,而不是构造函数。如果该函数是用作构造函数的,那么这种调用方式是一种常见错误。通常情况下,构造函数不会返回任何东西,因此,像普通函数一样调用构造函数,只会返回`undefined`赋给用作实例的变量。 335 | 336 | `var person = new Person()`使用`new`操作符,创建`Person`对象的实例,该实例继承自`Person.prototype`。另外一种方式是使用`Object.create`,例如:Object.create(Person.prototype)`。 337 | 338 | ```js 339 | function Person(name) { 340 | this.name = name; 341 | } 342 | 343 | var person = Person('John'); 344 | console.log(person); // undefined 345 | console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined 346 | 347 | var person = new Person('John'); 348 | console.log(person); // Person { name: "John" } 349 | console.log(person.name); // "john" 350 | ``` 351 | 352 | ###### 参考 353 | 354 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new 355 | 356 | [[↑] 回到顶部](#js-问题) 357 | 358 | ### `.call`和`.apply`有什么区别? 359 | 360 | `.call`和`.apply`都用于调用函数,第一个参数将用作函数内 this 的值。然而,`.call`接受逗号分隔的参数作为后面的参数,而`.apply`接受一个参数数组作为后面的参数。一个简单的记忆方法是,从`call`中的 C 联想到逗号分隔(comma-separated),从`apply`中的 A 联想到数组(array)。 361 | 362 | ```js 363 | function add(a, b) { 364 | return a + b; 365 | } 366 | 367 | console.log(add.call(null, 1, 2)); // 3 368 | console.log(add.apply(null, [1, 2])); // 3 369 | ``` 370 | 371 | [[↑] 回到顶部](#js-问题) 372 | 373 | ### 请说明`Function.prototype.bind`的用法。 374 | 375 | 摘自[MDN](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind): 376 | 377 | > `bind()`方法创建一个新的函数, 当被调用时,将其 this 关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。 378 | 379 | 根据我的经验,将`this`的值绑定到想要传递给其他函数的类的方法中是非常有用的。在 React 组件中经常这样做。 380 | 381 | ###### 参考 382 | 383 | * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind 384 | 385 | [[↑] 回到顶部](#js-问题) 386 | 387 | ### 什么时候会用到`document.write()`? 388 | 389 | `document.write()`用来将一串文本写入由`document.open()`打开的文档流中。当页面加载后执行`document.write()`时,它将调用`document.open`,会清除整个文档(``和``会被移除!),并将文档内容替换成给定的字符串参数。因此它通常被认为是危险的并且容易被误用。 390 | 391 | 网上有一些答案,解释了`document.write()`被用于分析代码中,或者[当你想包含只有在启用了 JavaScript 的情况下才能工作的样式](https://www.quirksmode.org/blog/archives/2005/06/three_javascrip_1.html)。它甚至在 HTML5 样板代码中用于[并行加载脚本并保持执行顺序](https://github.com/paulirish/html5-boilerplate/wiki/Script-Loading-Techniques#documentwrite-script-tag)!但是,我怀疑这些使用原因是过时的,现在可以在不使用`document.write()`的情况下实现。如果我的观点有错,请纠正我。 392 | 393 | ###### 参考 394 | 395 | * https://www.quirksmode.org/blog/archives/2005/06/three_javascrip_1.html 396 | * https://github.com/h5bp/html5-boilerplate/wiki/Script-Loading-Techniques#documentwrite-script-tag 397 | 398 | [[↑] 回到顶部](#js-问题) 399 | 400 | ### 功能检测(feature detection)、功能推断(feature inference)和使用 UA 字符串之间有什么区别? 401 | 402 | **功能检测(feature detection)** 403 | 404 | 功能检测包括确定浏览器是否支持某段代码,以及是否运行不同的代码(取决于它是否执行),以便浏览器始终能够正常运行代码功能,而不会在某些浏览器中出现崩溃和错误。例如: 405 | 406 | ```js 407 | if ('geolocation' in navigator) { 408 | // 可以使用 navigator.geolocation 409 | } else { 410 | // 处理 navigator.geolocation 功能缺失 411 | } 412 | ``` 413 | 414 | [Modernizr](https://modernizr.com/)是处理功能检测的优秀工具。 415 | 416 | **功能推断(feature inference)** 417 | 418 | 功能推断与功能检测一样,会对功能可用性进行检查,但是在判断通过后,还会使用其他功能,因为它假设其他功能也可用,例如: 419 | 420 | ```js 421 | if (document.getElementsByTagName) { 422 | element = document.getElementById(id); 423 | } 424 | ``` 425 | 426 | 非常不推荐这种方式。功能检测更能保证万无一失。 427 | 428 | **UA 字符串** 429 | 430 | 这是一个浏览器报告的字符串,它允许网络协议对等方(network protocol peers)识别请求用户代理的应用类型、操作系统、应用供应商和应用版本。它可以通过`navigator.userAgent`访问。 然而,这个字符串很难解析并且很可能存在欺骗性。例如,Chrome 会同时作为 Chrome 和 Safari 进行报告。因此,要检测 Safari,除了检查 Safari 字符串,还要检查是否存在 Chrome 字符串。不要使用这种方式。 431 | 432 | ###### 参考 433 | 434 | * https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection 435 | * https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th 436 | * https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent 437 | 438 | [[↑] 回到顶部](#js-问题) 439 | 440 | ### 请尽可能详细地解释 Ajax。 441 | 442 | Ajax(asynchronous JavaScript and XML)是使用客户端上的许多 Web 技术,创建异步 Web 应用的一种 Web 开发技术。借助 Ajax,Web 应用可以异步(在后台)向服务器发送数据和从服务器检索数据,而不会干扰现有页面的显示和行为。通过将数据交换层与表示层分离,Ajax 允许网页和扩展 Web 应用程序动态更改内容,而无需重新加载整个页面。实际上,现在通常将 XML 替换为 JSON,因为 JavaScript 对 JSON 有原生支持优势。 443 | 444 | `XMLHttpRequest` API 经常用于异步通信。此外还有最近流行的`fetch` API。 445 | 446 | ###### 参考 447 | 448 | * https://en.wikipedia.org/wiki/Ajax_(programming) 449 | * https://developer.mozilla.org/en-US/docs/AJAX 450 | 451 | [[↑] 回到顶部](#js-问题) 452 | 453 | ### 使用 Ajax 的优缺点分别是什么? 454 | 455 | **优点** 456 | 457 | * 交互性更好。来自服务器的新内容可以动态更改,无需重新加载整个页面。 458 | * 减少与服务器的连接,因为脚本和样式只需要被请求一次。 459 | * 状态可以维护在一个页面上。JavaScript 变量和 DOM 状态将得到保持,因为主容器页面未被重新加载。 460 | * 基本上包括大部分 SPA 的优点。 461 | 462 | **缺点** 463 | 464 | * 动态网页很难收藏。 465 | * 如果 JavaScript 已在浏览器中被禁用,则不起作用。 466 | * 有些网络爬虫不执行 JavaScript,也不会看到 JavaScript 加载的内容。 467 | * 基本上包括大部分 SPA 的缺点。 468 | 469 | [[↑] 回到顶部](#js-问题) 470 | 471 | ### 请说明 JSONP 的工作原理,它为什么不是真正的 Ajax? 472 | 473 | JSONP(带填充的 JSON)是一种通常用于绕过 Web 浏览器中的跨域限制的方法,因为 Ajax 不允许跨域请求。 474 | 475 | JSONP 通过` 484 | 485 | 486 | ``` 487 | 488 | ```js 489 | // 文件加载自 https://example.com?callback=printData 490 | printData({ name: 'Yang Shun' }); 491 | ``` 492 | 493 | 客户端必须在其全局范围内具有`printData`函数,并且在收到来自跨域的响应时,该函数将由客户端执行。 494 | 495 | JSONP 可能具有一些安全隐患。由于 JSONP 是纯 JavaScript 实现,它可以完成 JavaScript 所能做的一切,因此需要信任 JSONP 数据的提供者。 496 | 497 | 现如今,[跨来源资源共享(CORS)](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) 是推荐的主流方式,JSONP 已被视为一种比较 hack 的方式。 498 | 499 | ###### 参考 500 | 501 | * https://stackoverflow.com/a/2067584/1751946 502 | 503 | [[↑] 回到顶部](#js-问题) 504 | 505 | ### 你使用过 JavaScript 模板吗?用过什么相关的库? 506 | 507 | 使用过。Handlebars、Underscore、Lodash、AngularJS 和 JSX。我不喜欢 AngularJS 中的模板,因为它在指令中大量使用了字符串,并且书写错误会被忽略。JSX 是我的新宠,因为它更接近 JavaScript,几乎没有什么学习成本。现在,可以使用 ES2015 模板字符串快速创建模板,而不需依赖第三方代码。 508 | 509 | ```js 510 | const template = `
My name is: ${name}
`; 511 | ``` 512 | 513 | 但是,请注意上述方法中可能存在的 XSS,因为内容不会被转义,与模板库不同。 514 | 515 | [[↑] 回到顶部](#js-问题) 516 | 517 | ### 请解释变量提升(hoisting)。 518 | 519 | 变量提升(hoisting)是用于解释代码中变量声明行为的术语。使用`var`关键字声明或初始化的变量,会将声明语句“提升”到当前作用域的顶部。 但是,只有声明才会触发提升,赋值语句(如果有的话)将保持原样。我们用几个例子来解释一下。 520 | 521 | ```js 522 | // 用 var 声明得到提升 523 | console.log(foo); // undefined 524 | var foo = 1; 525 | console.log(foo); // 1 526 | 527 | // 用 let/const 声明不会提升 528 | console.log(bar); // ReferenceError: bar is not defined 529 | let bar = 2; 530 | console.log(bar); // 2 531 | ``` 532 | 533 | 函数声明会使函数体提升,但函数表达式(以声明变量的形式书写)只有变量声明会被提升。 534 | 535 | ```js 536 | // 函数声明 537 | console.log(foo); // [Function: foo] 538 | foo(); // 'FOOOOO' 539 | function foo() { 540 | console.log('FOOOOO'); 541 | } 542 | console.log(foo); // [Function: foo] 543 | 544 | // 函数表达式 545 | console.log(bar); // undefined 546 | bar(); // Uncaught TypeError: bar is not a function 547 | var bar = function() { 548 | console.log('BARRRR'); 549 | }; 550 | console.log(bar); // [Function: bar] 551 | ``` 552 | 553 | [[↑] 回到顶部](#js-问题) 554 | 555 | ### 请描述事件冒泡。 556 | 557 | 当一个事件在 DOM 元素上触发时,如果有事件监听器,它将尝试处理该事件,然后事件冒泡到其父级元素,并发生同样的事情。最后直到事件到达祖先元素。事件冒泡是实现事件委托的原理(event delegation)。 558 | 559 | [[↑] 回到顶部](#js-问题) 560 | 561 | ### “attribute” 和 “property” 之间有什么区别? 562 | 563 | “Attribute” 是在 HTML 中定义的,而 “property” 是在 DOM 上定义的。为了说明区别,假设我们在 HTML 中有一个文本框:``。 564 | 565 | ```js 566 | const input = document.querySelector('input'); 567 | console.log(input.getAttribute('value')); // Hello 568 | console.log(input.value); // Hello 569 | ``` 570 | 571 | 但是在文本框中键入“ World!”后: 572 | 573 | ```js 574 | console.log(input.getAttribute('value')); // Hello 575 | console.log(input.value); // Hello World! 576 | ``` 577 | 578 | ###### 参考 579 | 580 | * https://stackoverflow.com/questions/6003819/properties-and-attributes-in-html 581 | 582 | [[↑] 回到顶部](#js-问题) 583 | 584 | ### 为什么扩展 JavaScript 内置对象是不好的做法? 585 | 586 | 扩展 JavaScript 内置(原生)对象意味着将属性或方法添加到其`prototype`中。虽然听起来很不错,但事实上这样做很危险。想象一下,你的代码使用了一些库,它们通过添加相同的 contains 方法来扩展`Array.prototype`,如果这两个方法的行为不相同,那么这些实现将会相互覆盖,你的代码将不能正常运行。 587 | 588 | 扩展内置对象的唯一使用场景是创建 polyfill,本质上为老版本浏览器缺失的方法提供自己的实现,该方法是由 JavaScript 规范定义的。 589 | 590 | ###### 参考 591 | 592 | * http://lucybain.com/blog/2014/js-extending-built-in-objects/ 593 | 594 | [[↑] 回到顶部](#js-问题) 595 | 596 | ### document 中的`load`事件和`DOMContentLoaded`事件之间的区别是什么? 597 | 598 | 当初始的 HTML 文档被完全加载和解析完成之后,`DOMContentLoaded`事件被触发,而无需等待样式表、图像和子框架的完成加载。 599 | 600 | `window`的`load`事件仅在 DOM 和所有相关资源全部完成加载后才会触发。 601 | 602 | ###### 参考 603 | 604 | * https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded 605 | * https://developer.mozilla.org/en-US/docs/Web/Events/load 606 | 607 | [[↑] 回到顶部](#js-问题) 608 | 609 | ### `==`和`===`的区别是什么? 610 | 611 | `==`是抽象相等运算符,而`===`是严格相等运算符。`==`运算符是在进行必要的类型转换后,再比较。`===`运算符不会进行类型转换,所以如果两个值不是相同的类型,会直接返回`false`。使用`==`时,可能发生一些特别的事情,例如: 612 | 613 | ```js 614 | 1 == '1'; // true 615 | 1 == [1]; // true 616 | 1 == true; // true 617 | 0 == ''; // true 618 | 0 == '0'; // true 619 | 0 == false; // true 620 | ``` 621 | 622 | 我的建议是从不使用`==`运算符,除了方便与`null`或`undefined`比较时,`a == null`如果`a`为`null`或`undefined`将返回`true`。 623 | 624 | ```js 625 | var a = null; 626 | console.log(a == null); // true 627 | console.log(a == undefined); // true 628 | ``` 629 | 630 | ###### 参考 631 | 632 | * https://stackoverflow.com/questions/359494/which-equals-operator-vs-should-be-used-in-javascript-comparisons 633 | 634 | [[↑] 回到顶部](#js-问题) 635 | 636 | ### 请解释关于 JavaScript 的同源策略。 637 | 638 | 同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。 639 | 640 | ###### 参考 641 | 642 | * https://en.wikipedia.org/wiki/Same-origin_policy 643 | 644 | [[↑] 回到顶部](#js-问题) 645 | 646 | ### 请使下面的语句生效: 647 | 648 | ```js 649 | duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5] 650 | ``` 651 | 652 | ```js 653 | function duplicate(arr) { 654 | return arr.concat(arr); 655 | } 656 | 657 | duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5] 658 | ``` 659 | 660 | [[↑] 回到顶部](#js-问题) 661 | 662 | ### 请说明三元表达式中“三元”这个词代表什么? 663 | 664 | “三元”表示接受三个操作数:判断条件,`then`表达式和`else`表达式。三元表达式不是 JavaScript 特有的,我不知道这个问题为什么会出现在这里。 665 | 666 | ###### 参考 667 | 668 | * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator 669 | 670 | [[↑] 回到顶部](#js-问题) 671 | 672 | ### 什么是`"use strict";`?使用它有什么优缺点? 673 | 674 | 'use strict' 是用于对整个脚本或单个函数启用严格模式的语句。严格模式是可选择的一个限制 JavaScript 的变体一种方式 。 675 | 676 | **优点:** 677 | 678 | * 无法再意外创建全局变量。 679 | * 会使引起静默失败(silently fail,即:不报错也没有任何效果)的赋值操抛出异常。 680 | * 试图删除不可删除的属性时会抛出异常(之前这种操作不会产生任何效果)。 681 | * 要求函数的参数名唯一。 682 | * 全局作用域下,`this`的值为`undefined`。 683 | * 捕获了一些常见的编码错误,并抛出异常。 684 | * 禁用令人困惑或欠佳的功能。 685 | 686 | **缺点:** 687 | 688 | * 缺失许多开发人员已经习惯的功能。 689 | * 无法访问`function.caller`和`function.arguments`。 690 | * 以不同严格模式编写的脚本合并后可能导致问题。 691 | 692 | 总的来说,我认为利大于弊,我从来不使用严格模式禁用的功能,因此我推荐使用严格模式。 693 | 694 | ###### 参考 695 | 696 | * http://2ality.com/2011/10/strict-mode-hatred.html 697 | * http://lucybain.com/blog/2014/js-use-strict/ 698 | 699 | [[↑] 回到顶部](#js-问题) 700 | 701 | ### 创建一个循环,从 1 迭代到 100,`3`的倍数时输出 "fizz",`5`的倍数时输出 "buzz",同时为`3`和`5`的倍数时输出 "fizzbuzz"。 702 | 703 | 来自 [Paul Irish](https://gist.github.com/jaysonrowe/1592432#gistcomment-790724)的 FizzBuzz。 704 | 705 | ```js 706 | for (let i = 1; i <= 100; i++) { 707 | let f = i % 3 == 0, 708 | b = i % 5 == 0; 709 | console.log(f ? (b ? 'FizzBuzz' : 'Fizz') : b ? 'Buzz' : i); 710 | } 711 | ``` 712 | 713 | 我不建议你在面试时写上面的代码。只要写得清晰即可。关于更多千奇百怪的 FizzBuzz 实现,请查看下面的参考链接。 714 | 715 | ###### 参考 716 | 717 | * https://gist.github.com/jaysonrowe/1592432 718 | 719 | [[↑] 回到顶部](#js-问题) 720 | 721 | ### 为什么不要使用全局作用域? 722 | 723 | 每个脚本都可以访问全局作用域,如果人人都使用全局命名空间来定义自己的变量,肯定会发生冲突。使用模块模式(IIFE)将变量封装在本地命名空间中。 724 | 725 | [[↑] 回到顶部](#js-问题) 726 | 727 | ### 为什么要使用`load`事件?这个事件有什么缺点吗?你知道一些代替方案吗,为什么使用它们? 728 | 729 | 在文档装载完成后会触发`load`事件。此时,在文档中的所有对象都在 DOM 中,所有图像、脚本、链接和子框架都完成了加载。 730 | 731 | DOM 事件`DOMContentLoaded`将在页面的 DOM 构建完成后触发,但不要等待其他资源完成加载。如果在初始化之前不需要装入整个页面,这个事件是使用首选。 732 | 733 | TODO. 734 | 735 | ###### 参考 736 | 737 | * https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload 738 | 739 | [[↑] 回到顶部](#js-问题) 740 | 741 | ### 请解释单页应用是什么,如何使其对 SEO 友好。 742 | 743 | 以下摘自 [Grab Front End Guide](https://github.com/grab/front-end-guide),碰巧的是,这正是我自己写的! 744 | 745 | 现如今,Web 开发人员将他们构建的产品称为 Web 应用,而不是网站。虽然这两个术语之间没有严格的区别,但网络应用往往具有高度的交互性和动态性,允许用户执行操作并接收他们的操作响应。在过去,浏览器从服务器接收 HTML 并渲染。当用户导航到其它 URL 时,需要整页刷新,服务器会为新页面发送新的 HTML。这被称为服务器端渲染。 746 | 747 | 然而,在现代的 SPA 中,客户端渲染取而代之。浏览器从服务器加载初始页面、整个应用程序所需的脚本(框架、库、应用代码)和样式表。当用户导航到其他页面时,不会触发页面刷新。该页面的 URL 通过 [HTML5 History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) 进行更新。浏览器通过 [AJAX](https://developer.mozilla.org/en-US/docs/AJAX/Getting_Started) 请求向服务器检索新页面所需的数据(通常采用 JSON 格式)。然后,SPA 通过 JavaScript 来动态更新页面,这些 JavaScript 在初始页面加载时已经下载。这种模式类似于原生移动应用的工作方式。 748 | 749 | **好处:** 750 | 751 | * 用户感知响应更快,用户切换页面时,不再看到因页面刷新而导致的白屏。 752 | * 对服务器进行的 HTTP 请求减少,因为对于每个页面加载,不必再次下载相同的资源。 753 | * 客户端和服务器之间的关注点分离。可以为不同平台(例如手机、聊天机器人、智能手表)建立新的客户端,而无需修改服务器代码。只要 API 没有修改,可以单独修改客户端和服务器上的代码。 754 | 755 | **坏处:** 756 | 757 | * 由于加载了多个页面所需的框架、应用代码和资源,导致初始页面加载时间较长。 758 | * 服务器还需要进行额外的工作,需要将所有请求路由配置到单个入口点,然后由客户端接管路由。 759 | * SPA 依赖于 JavaScript 来呈现内容,但并非所有搜索引擎都在抓取过程中执行 JavaScript,他们可能会在你的页面上看到空的内容。这无意中损害了应用的搜索引擎优化(SEO)。然而,当你构建应用时,大多数情况下,搜索引擎优化并不是最重要的因素,因为并非所有内容都需要通过搜索引擎进行索引。为了解决这个问题,可以在服务器端渲染你的应用,或者使用诸如 [Prerender](https://prerender.io/) 的服务来“在浏览器中呈现你的 javascript,保存静态 HTML,并将其返回给爬虫”。 760 | 761 | ###### 参考 762 | 763 | * https://github.com/grab/front-end-guide#single-page-apps-spas 764 | * http://stackoverflow.com/questions/21862054/single-page-app-advantages-and-disadvantages 765 | * http://blog.isquaredsoftware.com/presentations/2016-10-revolution-of-web-dev/ 766 | * https://medium.freecodecamp.com/heres-why-client-side-rendering-won-46a349fadb52 767 | 768 | [[↑] 回到顶部](#js-问题) 769 | 770 | ### 你对 Promises 及其 polyfill 的掌握程度如何? 771 | 772 | 掌握它的工作原理。`Promise`是一个可能在未来某个时间产生结果的对象:操作成功的结果或失败的原因(例如发生网络错误)。 `Promise`可能处于以下三种状态之一:fulfilled、rejected 或 pending。 用户可以对`Promise`添加回调函数来处理操作成功的结果或失败的原因。 773 | 774 | 一些常见的 polyfill 是`$.deferred`、Q 和 Bluebird,但不是所有的 polyfill 都符合规范。ES2015 支持 Promises,现在通常不需要使用 polyfills。 775 | 776 | ###### 参考 777 | 778 | * https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261 779 | 780 | [[↑] 回到顶部](#js-问题) 781 | 782 | ### `Promise`代替回调函数有什么优缺点? 783 | 784 | **优点:** 785 | 786 | * 避免可读性极差的回调地狱。 787 | * 使用`.then()`编写的顺序异步代码,既简单又易读。 788 | * 使用`Promise.all()`编写并行异步代码变得很容易。 789 | 790 | **缺点:** 791 | 792 | * 轻微地增加了代码的复杂度(这点存在争议)。 793 | * 在不支持 ES2015 的旧版浏览器中,需要引入 polyfill 才能使用。 794 | 795 | [[↑] 回到顶部](#js-问题) 796 | 797 | ### 用转译成 JavaScript 的语言写 JavaScript 有什么优缺点? 798 | 799 | Some examples of languages that compile to JavaScript include CoffeeScript, Elm, ClojureScript, PureScript and TypeScript. 800 | 这些是转译成 JavaScript 的语言,包括 CoffeeScript、Elm、ClojureScript、PureScript 和 TypeScript。 801 | 802 | **优点:** 803 | 804 | * 修复了 JavaScript 中的一些长期问题,并摒弃了 JavaScript 不好的做法。 805 | * 在 JavaScript 的基础上提供一些语法糖,使我们能够编写更短的代码,我认为 ES5 缺乏语法糖的支持,但 ES2015 非常好。 806 | * 对于需要长时间维护的大型项目,静态类型非常好用(针对 TypeScript)。 807 | 808 | **缺点:** 809 | 810 | * 由于浏览器只运行 JavaScript,所以需要构建、编译过程,在将代码提供给浏览器之前,需要将代码转译为 JavaScript。 811 | * 如果 source map 不能很好地映射到预编译的源代码,调试会很痛苦。 812 | * 大多数开发人员不熟悉这些语言,需要学习它。如果将其用于项目,会增加团队成本。 813 | * 社区比较小(取决于语言),这意味着资源、教程、图书和工具难以找到。 814 | * 可能缺乏 IDE(编辑器)的支持。 815 | * 这些语言将始终落后于最新的 JavaScript 标准。 816 | * 开发人员应该清楚代码正在被编译到什么地方——因为这是实际运行的内容,是最重要的。 817 | 818 | 实际上,ES2015 已经大大改进了 JavaScript,编写体验很好。我现在还没有真正看到对 CoffeeScript 的需求。 819 | 820 | ###### 参考 821 | 822 | * https://softwareengineering.stackexchange.com/questions/72569/what-are-the-pros-and-cons-of-coffeescript 823 | 824 | [[↑] 回到顶部](#js-问题) 825 | 826 | ### 你使用什么工具和技巧调试 JavaScript 代码? 827 | 828 | * React 和 Redux 829 | * [React Devtools](https://github.com/facebook/react-devtools) 830 | * [Redux Devtools](https://github.com/gaearon/redux-devtools) 831 | * Vue 832 | * [Vue Devtools](https://github.com/vuejs/vue-devtools) 833 | * JavaScript 834 | * [Chrome Devtools](https://hackernoon.com/twelve-fancy-chrome-devtools-tips-dc1e39d10d9d) 835 | * `debugger`声明 836 | * 使用万金油`console.log`进行调试 837 | 838 | ###### 参考 839 | 840 | * https://hackernoon.com/twelve-fancy-chrome-devtools-tips-dc1e39d10d9d 841 | * https://raygun.com/blog/javascript-debugging/ 842 | 843 | [[↑] 回到顶部](#js-问题) 844 | 845 | ### 你使用什么语句遍历对象的属性和数组的元素? 846 | 847 | **对象:** 848 | 849 | * `for`循环:`for (var property in obj) { console.log(property); }`。但是,这还会遍历到它的继承属性,在使用之前,你需要加入`obj.hasOwnProperty(property)`检查。 850 | * `Object.keys()`:`Object.keys(obj).forEach(function (property) { ... })`。`Object.keys()`方法会返回一个由一个给定对象的自身可枚举属性组成的数组。 851 | * `Object.getOwnPropertyNames()`:`Object.getOwnPropertyNames(obj).forEach(function (property) { ... })`。`Object.getOwnPropertyNames()`方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。 852 | 853 | **数组:** 854 | 855 | * `for` loops:`for (var i = 0; i < arr.length; i++)`。这里的常见错误是`var`是函数作用域而不是块级作用域,大多数时候你想要迭代变量在块级作用域中。ES2015 引入了具有块级作用域的`let`,建议使用它。所以就变成了:`for (let i = 0; i < arr.length; i++)`。 856 | * `forEach`:`arr.forEach(function (el, index) { ... })`。这个语句结构有时会更精简,因为如果你所需要的只是数组元素,你不必使用`index`。还有`every`和`some`方法可以让你提前终止遍历。 857 | 858 | 大多数情况下,我更喜欢`.forEach`方法,但这取决于你想要做什么。`for`循环有更强的灵活性,比如使用`break`提前终止循环,或者递增步数大于一。 859 | 860 | [[↑] 回到顶部](#js-问题) 861 | 862 | ### 请解释可变对象和不可变对象之间的区别。 863 | 864 | * 什么是 JavaScript 中的不可变对象的例子? 865 | * 不变性有什么优点和缺点? 866 | * 你如何在自己的代码中实现不变性? 867 | 868 | **_可变对象_** 在创建之后是可以被改变的。 869 | 870 | **_不可变对象_** 在创建之后是不可以被改变的。 871 | 872 | 1. 在 `JavaScript` 中,`string` 和 `number` 从设计之初就是不可变(Immutable)。 873 | 2. **_不可变_** 其实是保持一个对象状态不变,这样做的好处是使得开发更加简单,可回溯,测试友好,减少了任何可能的副作用。但是,每当你想添加点东西到一个不可变(Immutable)对象里时,它一定是先拷贝已存在的值到新实例里,然后再给新实例添加内容,最后返回新实例。相比可变对象,这势必会有更多内存、计算量消耗。 874 | 3. 比如:构造一个纯函数 875 | 876 | ```js 877 | const student1 = { 878 | school: 'Baidu', 879 | name: 'HOU Ce', 880 | birthdate: '1995-12-15', 881 | }; 882 | 883 | const changeStudent = (student, newName, newBday) => { 884 | return { 885 | ...student, // 使用解构 886 | name: newName, // 覆盖name属性 887 | birthdate: newBday, // 覆盖birthdate属性 888 | }; 889 | }; 890 | 891 | const student2 = changeStudent(student1, 'YAN Haijing', '1990-11-10'); 892 | 893 | // both students will have the name properties 894 | console.log(student1, student2); 895 | // Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15"} 896 | // Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"} 897 | ``` 898 | 899 | ###### 参考 900 | 901 | * https://juejin.im/post/58d0ff6f1b69e6006b8fd4e9 902 | * https://www.interviewcake.com/concept/java/mutable 903 | * https://www.sitepoint.com/immutability-javascript/ 904 | 905 | [[↑] 回到顶部](#js-问题) 906 | 907 | ### 请解释同步和异步函数之间的区别。 908 | 909 | 同步函数阻塞,而异步函数不阻塞。在同步函数中,语句完成后,下一句才执行。在这种情况下,程序可以按照语句的顺序进行精确评估,如果其中一个语句需要很长时间,程序的执行会停滞很长时间。 910 | 911 | 异步函数通常接受回调作为参数,在调用异步函数后立即继续执行下一行。回调函数仅在异步操作完成且调用堆栈为空时调用。诸如从 Web 服务器加载数据或查询数据库等重负载操作应该异步完成,以便主线程可以继续执行其他操作,而不会出现一直阻塞,直到费时操作完成的情况(在浏览器中,界面会卡住)。 912 | 913 | [[↑] 回到顶部](#js-问题) 914 | 915 | ### 什么是事件循环?调用堆栈和任务队列之间有什么区别? 916 | 917 | 事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工作即将在任务队列中完成。如果调用堆栈为空并且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。 918 | 919 | 如果你没有看过 Philip Robert [关于事件循环的演讲](https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html),你应该看一下。这是观看次数最多的 JavaScript 相关视频之一。 920 | 921 | ###### 参考 922 | 923 | * https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html 924 | * http://theproactiveprogrammer.com/javascript/the-javascript-event-loop-a-stack-and-a-queue/ 925 | 926 | [[↑] 回到顶部](#js-问题) 927 | 928 | ### 请解释`function foo() {}`和`var foo = function() {}`之间`foo`的用法上的区别。 929 | 930 | 前者是函数声明,后者是函数表达式。关键的区别在于函数声明会使函数体提升(具有与变量相同的提升行为),但函数表达式的函数体不能。有关变量提升的更多解释,请参阅上面关于变量提升的问题。如果你试图在定义函数表达式之前调用它,你会得到一个`Uncaught TypeError: XXX is not a function`的错误。 931 | 932 | **函数声明** 933 | 934 | ```js 935 | foo(); // 'FOOOOO' 936 | function foo() { 937 | console.log('FOOOOO'); 938 | } 939 | ``` 940 | 941 | **函数表达式** 942 | 943 | ```js 944 | foo(); // Uncaught TypeError: foo is not a function 945 | var foo = function() { 946 | console.log('FOOOOO'); 947 | }; 948 | ``` 949 | 950 | ###### 参考 951 | 952 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function 953 | 954 | [[↑] 回到顶部](#js-问题) 955 | 956 | ### 使用`let`、`var`和`const`创建变量有什么区别? 957 | 958 | 用`var`声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。`let`和`const`是块级作用域,意味着它们只能在最近的一组花括号(function、if-else 代码块或 for 循环中)中访问。 959 | 960 | ```js 961 | function foo() { 962 | // 所有变量在函数中都可访问 963 | var bar = 'bar'; 964 | let baz = 'baz'; 965 | const qux = 'qux'; 966 | 967 | console.log(bar); // bar 968 | console.log(baz); // baz 969 | console.log(qux); // qux 970 | } 971 | 972 | console.log(bar); // ReferenceError: bar is not defined 973 | console.log(baz); // ReferenceError: baz is not defined 974 | console.log(qux); // ReferenceError: qux is not defined 975 | ``` 976 | 977 | ```js 978 | if (true) { 979 | var bar = 'bar'; 980 | let baz = 'baz'; 981 | const qux = 'qux'; 982 | } 983 | 984 | // 用 var 声明的变量在函数作用域上都可访问 985 | console.log(bar); // bar 986 | // let 和 const 定义的变量在它们被定义的语句块之外不可访问 987 | console.log(baz); // ReferenceError: baz is not defined 988 | console.log(qux); // ReferenceError: qux is not defined 989 | ``` 990 | 991 | `var`会使变量提升,这意味着变量可以在声明之前使用。`let`和`const`不会使变量提升,提前使用会报错。 992 | 993 | ```js 994 | console.log(foo); // undefined 995 | 996 | var foo = 'foo'; 997 | 998 | console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization 999 | 1000 | let baz = 'baz'; 1001 | 1002 | console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization 1003 | 1004 | const bar = 'bar'; 1005 | ``` 1006 | 1007 | 用`var`重复声明不会报错,但`let`和`const`会。 1008 | 1009 | ```js 1010 | var foo = 'foo'; 1011 | var foo = 'bar'; 1012 | console.log(foo); // "bar" 1013 | 1014 | let baz = 'baz'; 1015 | let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared 1016 | ``` 1017 | 1018 | `let`和`const`的区别在于:`let`允许多次赋值,而`const`只允许一次。 1019 | 1020 | ```js 1021 | // 这样不会报错。 1022 | let foo = 'foo'; 1023 | foo = 'bar'; 1024 | 1025 | // 这样会报错。 1026 | const baz = 'baz'; 1027 | baz = 'qux'; 1028 | ``` 1029 | 1030 | ###### 参考 1031 | 1032 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let 1033 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var 1034 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const 1035 | 1036 | [[↑] 回到顶部](#js-问题) 1037 | 1038 | ### ES6 的类和 ES5 的构造函数有什么区别? 1039 | 1040 | TODO 1041 | 1042 | [[↑] 回到顶部](#js-问题) 1043 | 1044 | ### 你能给出一个使用箭头函数的例子吗,箭头函数与其他函数有什么不同? 1045 | 1046 | TODO 1047 | 1048 | [[↑] 回到顶部](#js-问题) 1049 | 1050 | ### 在构造函数中使用箭头函数有什么好处? 1051 | 1052 | TODO 1053 | 1054 | [[↑] 回到顶部](#js-问题) 1055 | 1056 | ### 高阶函数(higher-order)的定义是什么? 1057 | 1058 | 高阶函数是将一个或多个函数作为参数的函数,它用于数据处理,也可能将函数作为返回结果。高阶函数是为了抽象一些重复执行的操作。一个典型的例子是`map`,它将一个数组和一个函数作为参数。`map`使用这个函数来转换数组中的每个元素,并返回一个包含转换后元素的新数组。JavaScript 中的其他常见示例是`forEach`、`filter`和`reduce`。高阶函数不仅需要操作数组的时候会用到,还有许多函数返回新函数的用例。`Function.prototype.bind`就是一个例子。 1059 | 1060 | **Map 示例:** 1061 | 1062 | 假设我们有一个由名字组成的数组,我们需要将每个字符转换为大写字母。 1063 | 1064 | ```js 1065 | const names = ['irish', 'daisy', 'anna']; 1066 | ``` 1067 | 1068 | 不使用高阶函数的方法是这样: 1069 | 1070 | ```js 1071 | const transformNamesToUppercase = function(names) { 1072 | const results = []; 1073 | for (let i = 0; i < names.length; i++) { 1074 | results.push(names[i].toUpperCase()); 1075 | } 1076 | return results; 1077 | }; 1078 | transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA'] 1079 | ``` 1080 | 1081 | 使用`.map(transformerFn)`使代码更简明 1082 | 1083 | ```js 1084 | const transformNamesToUppercase = function(names) { 1085 | return names.map(name => name.toUpperCase()); 1086 | }; 1087 | transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA'] 1088 | ``` 1089 | 1090 | ###### 参考 1091 | 1092 | * https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99 1093 | * https://hackernoon.com/effective-functional-javascript-first-class-and-higher-order-functions-713fde8df50a 1094 | * https://eloquentjavascript.net/05_higher_order.html 1095 | 1096 | [[↑] 回到顶部](#js-问题) 1097 | 1098 | ### 请给出一个解构(destructuring)对象或数组的例子。 1099 | 1100 | 解构是 ES6 中新功能,它提供了一种简洁方便的方法来提取对象或数组的值,并将它们放入不同的变量中。 1101 | 1102 | **数组解构** 1103 | 1104 | ```js 1105 | // 变量赋值 1106 | const foo = ['one', 'two', 'three']; 1107 | 1108 | const [one, two, three] = foo; 1109 | console.log(one); // "one" 1110 | console.log(two); // "two" 1111 | console.log(three); // "three" 1112 | ``` 1113 | 1114 | ```js 1115 | // 变量交换 1116 | let a = 1; 1117 | let b = 3; 1118 | 1119 | [a, b] = [b, a]; 1120 | console.log(a); // 3 1121 | console.log(b); // 1 1122 | ``` 1123 | 1124 | **对象解构** 1125 | 1126 | ```js 1127 | // 变量赋值 1128 | const o = { p: 42, q: true }; 1129 | const { p, q } = o; 1130 | 1131 | console.log(p); // 42 1132 | console.log(q); // true 1133 | ``` 1134 | 1135 | ###### 参考 1136 | 1137 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment 1138 | * https://ponyfoo.com/articles/es6-destructuring-in-depth 1139 | 1140 | [[↑] 回到顶部](#js-问题) 1141 | 1142 | ### ES6 的模板字符串为生成字符串提供了很大的灵活性,你可以举个例子吗? 1143 | 1144 | **_模板字面量_**(Template literals) 是允许嵌入表达式的字符串字面量。你可以使用多行字符串和字符串插值功能。 1145 | 1146 | **语法** 1147 | 1148 | ```js 1149 | `string text``string text line 1 1150 | string text line 2``string text ${expression} string text`; 1151 | 1152 | tag`string text ${expression} string text`; 1153 | ``` 1154 | 1155 | **示例** 1156 | 1157 | ```js 1158 | console.log(`string text line 1 1159 | string text line 2`); 1160 | // "string text line 1 1161 | // string text line 2" 1162 | 1163 | var a = 5; 1164 | var b = 10; 1165 | console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`); 1166 | // "Fifteen is 15 and 1167 | // not 20." 1168 | ``` 1169 | 1170 | ```js 1171 | //show函数采用rest参数的写法如下: 1172 | 1173 | let name = '张三', 1174 | age = 20, 1175 | message = show`我来给大家介绍:${name}的年龄是${age}.`; 1176 | 1177 | function show(stringArr, ...values) { 1178 | let output = ''; 1179 | 1180 | let index = 0; 1181 | 1182 | for (; index < values.length; index++) { 1183 | output += stringArr[index] + values[index]; 1184 | } 1185 | 1186 | output += stringArr[index]; 1187 | 1188 | return output; 1189 | } 1190 | 1191 | message; //"我来给大家介绍:张三的年龄是20." 1192 | ``` 1193 | 1194 | ###### 参考 1195 | 1196 | * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings 1197 | 1198 | [[↑] 回到顶部](#js-问题) 1199 | 1200 | ### 你能举出一个柯里化函数(curry function)的例子吗?它有哪些好处? 1201 | 1202 | 柯里化(currying)是一种模式,其中具有多个参数的函数被分解为多个函数,当被串联调用时,将一次一个地累积所有需要的参数。这种技术帮助编写函数式风格的代码,使代码更易读、紧凑。值得注意的是,对于需要被 curry 的函数,它需要从一个函数开始,然后分解成一系列函数,每个函数都需要一个参数。 1203 | 1204 | ```js 1205 | function curry(fn) { 1206 | if (fn.length === 0) { 1207 | return fn; 1208 | } 1209 | 1210 | function _curried(depth, args) { 1211 | return function(newArgument) { 1212 | if (depth - 1 === 0) { 1213 | return fn(...args, newArgument); 1214 | } 1215 | return _curried(depth - 1, [...args, newArgument]); 1216 | }; 1217 | } 1218 | 1219 | return _curried(fn.length, []); 1220 | } 1221 | 1222 | function add(a, b) { 1223 | return a + b; 1224 | } 1225 | 1226 | var curriedAdd = curry(add); 1227 | var addFive = curriedAdd(5); 1228 | 1229 | var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10] 1230 | ``` 1231 | 1232 | ###### 参考 1233 | 1234 | * https://hackernoon.com/currying-in-js-d9ddc64f162e 1235 | 1236 | [[↑] 回到顶部](#js-问题) 1237 | 1238 | ### 使用扩展运算符(spread)的好处是什么,它与使用剩余参数语句(rest)有什么区别? 1239 | 1240 | 在函数泛型编码时,ES6 的扩展运算符非常有用,因为我们可以轻松创建数组和对象的拷贝,而无需使用`Object.create`、`slice`或其他函数库。这个语言特性在 Redux 和 rx.js 的项目中经常用到。 1241 | 1242 | ```js 1243 | function putDookieInAnyArray(arr) { 1244 | return [...arr, 'dookie']; 1245 | } 1246 | 1247 | const result = putDookieInAnyArray(['I', 'really', "don't", 'like']); // ["I", "really", "don't", "like", "dookie"] 1248 | 1249 | const person = { 1250 | name: 'Todd', 1251 | age: 29, 1252 | }; 1253 | 1254 | const copyOfTodd = { ...person }; 1255 | ``` 1256 | 1257 | ES6 的剩余参数语句提供了一个简写,允许我们将不定数量的参数表示为一个数组。它就像是扩展运算符语法的反面,将数据收集到数组中,而不是解构数组。剩余参数语句在函数参数、数组和对象的解构赋值中有很大作用。 1258 | 1259 | ```js 1260 | function addFiveToABunchOfNumbers(...numbers) { 1261 | return numbers.map(x => x + 5); 1262 | } 1263 | 1264 | const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10); // [9, 10, 11, 12, 13, 14, 15] 1265 | 1266 | const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4] 1267 | 1268 | const { e, f, ...others } = { 1269 | e: 1, 1270 | f: 2, 1271 | g: 3, 1272 | h: 4, 1273 | }; // e: 1, f: 2, others: { g: 3, h: 4 } 1274 | ``` 1275 | 1276 | ###### 参考 1277 | 1278 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax 1279 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters 1280 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment 1281 | 1282 | [[↑] 回到顶部](#js-问题) 1283 | 1284 | ### 如何在文件之间共用代码? 1285 | 1286 | 这取决于执行 JavaScript 的环境。 1287 | 1288 | 在客户端(浏览器环境)上,只要变量或函数在全局作用域(`window`)中声明,所有脚本都可以引用它们。或者,通过 RequireJS 采用异步模块定义(AMD)以获得更多模块化方法。 1289 | 1290 | 在服务器(Node.js)上,常用的方法是使用 CommonJS。每个文件都被视为一个模块,可以通过将它们附加到`module.exports`对象来导出变量和函数。 1291 | 1292 | ES2015 定义了一个模块语法,旨在替换 AMD 和 CommonJS。 这最终将在浏览器和 Node 环境中得到支持。 1293 | 1294 | [[↑] 回到顶部](#js-问题) 1295 | 1296 | ###### 参考 1297 | 1298 | * http://requirejs.org/docs/whyamd.html 1299 | * https://nodejs.org/docs/latest/api/modules.html 1300 | * http://2ality.com/2014/09/es6-modules-final.html 1301 | 1302 | ### 什么情况下会用到静态类成员? 1303 | 1304 | 静态类成员(属性或方法)不绑定到某个类的特定实例,不管哪个实例引用它,都具有相同的值。静态属性通常是配置变量,而静态方法通常是纯粹的实用函数,不依赖于实例的状态。 1305 | 1306 | ###### 参考 1307 | 1308 | * https://stackoverflow.com/questions/21155438/when-to-use-static-variables-methods-and-when-to-use-instance-variables-methods 1309 | 1310 | [[↑] 回到顶部](#js-问题) 1311 | 1312 | ### 其他答案 1313 | 1314 | * http://flowerszhong.github.io/2013/11/20/javascript-questions.html 1315 | 1316 | ### 请用原生js实现一个函数,给页面制定的任意一个元素添加一个透明遮罩(透明度可变,默认0.2),使这个区域点击无效,要求兼容IE8+及各主流浏览器,遮罩层效果如下图所示: 1317 | ![遮罩效果](/img/element-mask.jp) 1318 | 1319 | ``` 1320 | 1328 | 1329 |
1330 | 1331 | 1363 | ``` 1364 | 1365 | ### 请用代码写出(今天是星期x)其中x表示当天是星期几,如果当天是星期一,输出应该是"今天是星期一" 1366 | 1367 | ``` 1368 | var days = ['日','一','二','三','四','五','六']; 1369 | var date = new Date(); 1370 | 1371 | console.log('今天是星期' + days[date.getDay()]); 1372 | ``` 1373 | 1374 | ### 下面这段代码想要循环延时输出结果0 1 2 3 4,请问输出结果是否正确,如果不正确,请说明为什么,并修改循环内的代码使其输出正确结果 1375 | 1376 | ``` 1377 | for (var i = 0; i < 5; ++i) { 1378 | setTimeout(function () { 1379 | console.log(i + ' '); 1380 | }, 100); 1381 | } 1382 | ``` 1383 | 1384 | 不能输出正确结果,因为循环中setTimeout接受的参数函数通过闭包访问变量i。javascript运行环境为单线程,setTimeout注册的函数需要等待线程空闲才能执行,此时for循环已经结束,i值为5.五个定时输出都是5 1385 | 修改方法:将setTimeout放在函数立即调用表达式中,将i值作为参数传递给包裹函数,创建新闭包 1386 | 1387 | ``` 1388 | for (var i = 0; i < 5; ++i) { 1389 | (function (i) { 1390 | setTimeout(function () { 1391 | console.log(i + ' '); 1392 | }, 100); 1393 | }(i)); 1394 | } 1395 | ``` 1396 | 1397 | 1398 | 1399 | ### 现有一个Page类,其原型对象上有许多以post开头的方法(如postMsg);另有一拦截函数chekc,只返回ture或false.请设计一个函数,该函数应批量改造原Page的postXXX方法,在保留其原有功能的同时,为每个postXXX方法增加拦截验证功能,当chekc返回true时继续执行原postXXX方法,返回false时不再执行原postXXX方法 1400 | 1401 | ``` 1402 | function Page() {} 1403 | 1404 | Page.prototype = { 1405 | constructor: Page, 1406 | 1407 | postA: function (a) { 1408 | console.log('a:' + a); 1409 | }, 1410 | postB: function (b) { 1411 | console.log('b:' + b); 1412 | }, 1413 | postC: function (c) { 1414 | console.log('c:' + c); 1415 | }, 1416 | check: function () { 1417 | return Math.random() > 0.5; 1418 | } 1419 | } 1420 | 1421 | function checkfy(obj) { 1422 | for (var key in obj) { 1423 | if (key.indexOf('post') === 0 && typeof obj[key] === 'function') { 1424 | (function (key) { 1425 | var fn = obj[key]; 1426 | obj[key] = function () { 1427 | if (obj.check()) { 1428 | fn.apply(obj, arguments); 1429 | } 1430 | }; 1431 | }(key)); 1432 | } 1433 | } 1434 | } // end checkfy() 1435 | 1436 | checkfy(Page.prototype); 1437 | 1438 | var obj = new Page(); 1439 | 1440 | obj.postA('checkfy'); 1441 | obj.postB('checkfy'); 1442 | obj.postC('checkfy'); 1443 | ``` 1444 | 1445 | ### 完成下面的tool-tip 1446 | ![xxx](img/tip-box.jpg) 1447 | 1448 | ### 编写javascript深度克隆函数deepClone 1449 | 1450 | function deepClone(obj) { 1451 | var _toString = Object.prototype.toString; 1452 | 1453 | // null, undefined, non-object, function 1454 | if (!obj || typeof obj !== 'object') { 1455 | return obj; 1456 | } 1457 | 1458 | // DOM Node 1459 | if (obj.nodeType && 'cloneNode' in obj) { 1460 | return obj.cloneNode(true); 1461 | } 1462 | 1463 | // Date 1464 | if (_toString.call(obj) === '[object Date]') { 1465 | return new Date(obj.getTime()); 1466 | } 1467 | 1468 | // RegExp 1469 | if (_toString.call(obj) === '[object RegExp]') { 1470 | var flags = []; 1471 | if (obj.global) { flags.push('g'); } 1472 | if (obj.multiline) { flags.push('m'); } 1473 | if (obj.ignoreCase) { flags.push('i'); } 1474 | 1475 | return new RegExp(obj.source, flags.join('')); 1476 | } 1477 | 1478 | var result = Array.isArray(obj) ? [] : 1479 | obj.constructor ? new obj.constructor() : {}; 1480 | 1481 | for (var key in obj ) { 1482 | result[key] = deepClone(obj[key]); 1483 | } 1484 | 1485 | return result; 1486 | } 1487 | 1488 | function A() { 1489 | this.a = a; 1490 | } 1491 | 1492 | var a = { 1493 | name: 'qiu', 1494 | birth: new Date(), 1495 | pattern: /qiu/gim, 1496 | container: document.body, 1497 | hobbys: ['book', new Date(), /aaa/gim, 111] 1498 | }; 1499 | 1500 | var c = new A(); 1501 | var b = deepClone(c); 1502 | console.log(c.a === b.a); 1503 | console.log(c, b); 1504 | 1505 | ### 补充代码,鼠标单击Button1后将Button1移动到Button2的后面 1506 | 1507 | 1508 | 1509 | 1510 | TEst 1511 | 1512 | 1513 | 1514 |
1515 | 1516 | 1517 |
1518 | 1519 | 1543 | 1544 | 1545 | 1546 | ### 网页中实现一个计算当年还剩多少时间的倒数计时程序,要求网页上实时动态显示"××年还剩××天××时××分××秒" 1547 | 1548 | 1549 | 1550 | 1551 | 1552 | TEst 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1602 | 1603 | 1604 | 1605 | 1606 | ### 完成一个函数,接受数组作为参数,数组元素为整数或者数组,数组元素包含整数或数组,函数返回扁平化后的数组 1607 | 如:[1, [2, [ [3, 4], 5], 6]] => [1, 2, 3, 4, 5, 6] 1608 | 1609 | ``` 1610 | var data = [1, [2, [ [3, 4], 5], 6]]; 1611 | 1612 | function flat(data, result) { 1613 | var i, d, len; 1614 | for (i = 0, len = data.length; i < len; ++i) { 1615 | d = data[i]; 1616 | if (typeof d === 'number') { 1617 | result.push(d); 1618 | } else { 1619 | flat(d, result); 1620 | } 1621 | } 1622 | } 1623 | 1624 | var result = []; 1625 | flat(data, result); 1626 | 1627 | console.log(result); 1628 | ``` 1629 | 1630 | ### 如何判断一个对象是否为数组 1631 | 如果浏览器支持Array.isArray()可以直接判断否则需进行必要判断 1632 | 1633 | ``` 1634 | /** 1635 | * 判断一个对象是否是数组,参数不是对象或者不是数组,返回false 1636 | * 1637 | * @param {Object} arg 需要测试是否为数组的对象 1638 | * @return {Boolean} 传入参数是数组返回true,否则返回false 1639 | */ 1640 | function isArray(arg) { 1641 | if (typeof arg === 'object') { 1642 | return Object.prototype.toString.call(arg) === '[object Array]'; 1643 | } 1644 | return false; 1645 | } 1646 | ``` 1647 | 1648 | ### 请评价以下事件监听器代码并给出改进意见 1649 | 1650 | ``` 1651 | if (window.addEventListener) { 1652 | var addListener = function (el, type, listener, useCapture) { 1653 | el.addEventListener(type, listener, useCapture); 1654 | }; 1655 | } 1656 | else if (document.all) { 1657 | addListener = function (el, type, listener) { 1658 | el.attachEvent('on' + type, function () { 1659 | listener.apply(el); 1660 | }); 1661 | }; 1662 | } 1663 | ``` 1664 | 1665 | 作用:浏览器功能检测实现跨浏览器DOM事件绑定 1666 | 1667 | 优点: 1668 | 1669 | 1. 测试代码只运行一次,根据浏览器确定绑定方法 1670 | 2. 通过``listener.apply(el)``解决IE下监听器this与标准不一致的地方 1671 | 3. 在浏览器不支持的情况下提供简单的功能,在标准浏览器中提供捕获功能 1672 | 1673 | 缺点: 1674 | 1675 | 1. document.all作为IE检测不可靠,应该使用if(el.attachEvent) 1676 | 2. addListener在不同浏览器下API不一样 1677 | 3. ``listener.apply``使this与标准一致但监听器无法移除 1678 | 4. 未解决IE下listener参数event。 target问题 1679 | 1680 | 改进: 1681 | 1682 | ``` 1683 | var addListener; 1684 | 1685 | if (window.addEventListener) { 1686 | addListener = function (el, type, listener, useCapture) { 1687 | el.addEventListener(type, listener, useCapture); 1688 | return listener; 1689 | }; 1690 | } 1691 | else if (window.attachEvent) { 1692 | addListener = function (el, type, listener) { 1693 | // 标准化this,event,target 1694 | var wrapper = function () { 1695 | var event = window.event; 1696 | event.target = event.srcElement; 1697 | listener.call(el, event); 1698 | }; 1699 | 1700 | el.attachEvent('on' + type, wrapper); 1701 | return wrapper; 1702 | // 返回wrapper。调用者可以保存,以后remove 1703 | }; 1704 | } 1705 | ``` 1706 | 1707 | ### 如何判断一个对象是否为函数 1708 | 1709 | ``` 1710 | /** 1711 | * 判断对象是否为函数,如果当前运行环境对可调用对象(如正则表达式) 1712 | * 的typeof返回'function',采用通用方法,否则采用优化方法 1713 | * 1714 | * @param {Any} arg 需要检测是否为函数的对象 1715 | * @return {boolean} 如果参数是函数,返回true,否则false 1716 | */ 1717 | function isFunction(arg) { 1718 | if (arg) { 1719 | if (typeof (/./) !== 'function') { 1720 | return typeof arg === 'function'; 1721 | } else { 1722 | return Object.prototype.toString.call(arg) === '[object Function]'; 1723 | } 1724 | } // end if 1725 | return false; 1726 | } 1727 | ``` 1728 | 1729 | ### 编写一个函数接受url中query string为参数,返回解析后的Object,query string使用application/x-www-form-urlencoded编码 1730 | 1731 | ``` 1732 | /** 1733 | * 解析query string转换为对象,一个key有多个值时生成数组 1734 | * 1735 | * @param {String} query 需要解析的query字符串,开头可以是?, 1736 | * 按照application/x-www-form-urlencoded编码 1737 | * @return {Object} 参数解析后的对象 1738 | */ 1739 | function parseQuery(query) { 1740 | var result = {}; 1741 | 1742 | // 如果不是字符串返回空对象 1743 | if (typeof query !== 'string') { 1744 | return result; 1745 | } 1746 | 1747 | // 去掉字符串开头可能带的? 1748 | if (query.charAt(0) === '?') { 1749 | query = query.substring(1); 1750 | } 1751 | 1752 | var pairs = query.split('&'); 1753 | var pair; 1754 | var key, value; 1755 | var i, len; 1756 | 1757 | for (i = 0, len = pairs.length; i < len; ++i) { 1758 | pair = pairs[i].split('='); 1759 | // application/x-www-form-urlencoded编码会将' '转换为+ 1760 | key = decodeURIComponent(pair[0]).replace(/\+/g, ' '); 1761 | value = decodeURIComponent(pair[1]).replace(/\+/g, ' '); 1762 | 1763 | // 如果是新key,直接添加 1764 | if (!(key in result)) { 1765 | result[key] = value; 1766 | } 1767 | // 如果key已经出现一次以上,直接向数组添加value 1768 | else if (isArray(result[key])) { 1769 | result[key].push(value); 1770 | } 1771 | // key第二次出现,将结果改为数组 1772 | else { 1773 | var arr = [result[key]]; 1774 | arr.push(value); 1775 | result[key] = arr; 1776 | } // end if-else 1777 | } // end for 1778 | 1779 | return result; 1780 | } 1781 | 1782 | function isArray(arg) { 1783 | if (arg && typeof arg === 'object') { 1784 | return Object.prototype.toString.call(arg) === '[object Array]'; 1785 | } 1786 | return false; 1787 | } 1788 | /** 1789 | console.log(parseQuery('sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8')); 1790 | */ 1791 | ``` 1792 | 1793 | ### 解析一个完整的url,返回Object包含域与window.location相同 1794 | 1795 | ``` 1796 | /** 1797 | * 解析一个url并生成window.location对象中包含的域 1798 | * location: 1799 | * { 1800 | * href: '包含完整的url', 1801 | * origin: '包含协议到pathname之前的内容', 1802 | * protocol: 'url使用的协议,包含末尾的:', 1803 | * username: '用户名', // 暂时不支持 1804 | * password: '密码', // 暂时不支持 1805 | * host: '完整主机名,包含:和端口', 1806 | * hostname: '主机名,不包含端口' 1807 | * port: '端口号', 1808 | * pathname: '服务器上访问资源的路径/开头', 1809 | * search: 'query string,?开头', 1810 | * hash: '#开头的fragment identifier' 1811 | * } 1812 | * 1813 | * @param {string} url 需要解析的url 1814 | * @return {Object} 包含url信息的对象 1815 | */ 1816 | function parseUrl(url) { 1817 | var result = {}; 1818 | var keys = ['href', 'origin', 'protocol', 'host', 1819 | 'hostname', 'port', 'pathname', 'search', 'hash']; 1820 | var i, len; 1821 | var regexp = /(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?/; 1822 | 1823 | var match = regexp.exec(url); 1824 | 1825 | if (match) { 1826 | for (i = keys.length - 1; i >= 0; --i) { 1827 | result[keys[i]] = match[i] ? match[i] : ''; 1828 | } 1829 | } 1830 | 1831 | return result; 1832 | } 1833 | ``` 1834 | 1835 | ### 完成函数getViewportSize返回指定窗口的视口尺寸 1836 | 1837 | ``` 1838 | /** 1839 | * 查询指定窗口的视口尺寸,如果不指定窗口,查询当前窗口尺寸 1840 | **/ 1841 | function getViewportSize(w) { 1842 | w = w || window; 1843 | 1844 | // IE9及标准浏览器中可使用此标准方法 1845 | if ('innerHeight' in w) { 1846 | return { 1847 | width: w.innerWidth, 1848 | height: w.innerHeight 1849 | }; 1850 | } 1851 | 1852 | var d = w.document; 1853 | // IE 8及以下浏览器在标准模式下 1854 | if (document.compatMode === 'CSS1Compat') { 1855 | return { 1856 | width: d.documentElement.clientWidth, 1857 | height: d.documentElement.clientHeight 1858 | }; 1859 | } 1860 | 1861 | // IE8及以下浏览器在怪癖模式下 1862 | return { 1863 | width: d.body.clientWidth, 1864 | height: d.body.clientHeight 1865 | }; 1866 | } 1867 | ``` 1868 | 1869 | ### 完成函数getScrollOffset返回窗口滚动条偏移量 1870 | 1871 | /** 1872 | * 获取指定window中滚动条的偏移量,如未指定则获取当前window 1873 | * 滚动条偏移量 1874 | * 1875 | * @param {window} w 需要获取滚动条偏移量的窗口 1876 | * @return {Object} obj.x为水平滚动条偏移量,obj.y为竖直滚动条偏移量 1877 | */ 1878 | function getScrollOffset(w) { 1879 | w = w || window; 1880 | // 如果是标准浏览器 1881 | if (w.pageXOffset != null) { 1882 | return { 1883 | x: w.pageXOffset, 1884 | y: w.pageYOffset 1885 | }; 1886 | } 1887 | 1888 | // 老版本IE,根据兼容性不同访问不同元素 1889 | var d = w.document; 1890 | if (d.compatMode === 'CSS1Compat') { 1891 | return { 1892 | x: d.documentElement.scrollLeft, 1893 | y: d.documentElement.scrollTop 1894 | } 1895 | } 1896 | 1897 | return { 1898 | x: d.body.scrollLeft, 1899 | y: d.body.scrollTop 1900 | }; 1901 | } 1902 | 1903 | 1904 | ### 现有一个字符串richText,是一段富文本,需要显示在页面上.有个要求,需要给其中只包含一个img元素的p标签增加一个叫pic的class.请编写代码实现.可以使用jQuery或KISSY. 1905 | 1906 | function richText(text) { 1907 | var div = document.createElement('div'); 1908 | div.innerHTML = text; 1909 | var p = div.getElementsByTagName('p'); 1910 | var i, len; 1911 | 1912 | for (i = 0, len = p.length; i < len; ++i) { 1913 | if (p[i].getElementsByTagName('img').length === 1) { 1914 | p[i].classList.add('pic'); 1915 | } 1916 | } 1917 | 1918 | return div.innerHTML; 1919 | } 1920 | 1921 | ### 请实现一个Event类,继承自此类的对象都会拥有两个方法on,off,once和trigger 1922 | 1923 | 1924 | function Event() { 1925 | if (!(this instanceof Event)) { 1926 | return new Event(); 1927 | } 1928 | this._callbacks = {}; 1929 | } 1930 | Event.prototype.on = function (type, handler) { 1931 | this_callbacks = this._callbacks || {}; 1932 | this._callbacks[type] = this.callbacks[type] || []; 1933 | this._callbacks[type].push(handler); 1934 | 1935 | return this; 1936 | }; 1937 | 1938 | Event.prototype.off = function (type, handler) { 1939 | var list = this._callbacks[type]; 1940 | 1941 | if (list) { 1942 | for (var i = list.length; i >= 0; --i) { 1943 | if (list[i] === handler) { 1944 | list.splice(i, 1); 1945 | } 1946 | } 1947 | } 1948 | 1949 | return this; 1950 | }; 1951 | 1952 | Event.prototype.trigger = function (type, data) { 1953 | var list = this._callbacks[type]; 1954 | 1955 | if (list) { 1956 | for (var i = 0, len = list.length; i < len; ++i) { 1957 | list[i].call(this, data); 1958 | } 1959 | } 1960 | }; 1961 | 1962 | Event.prototype.once = function (type, handler) { 1963 | var self = this; 1964 | 1965 | function wrapper() { 1966 | handler.apply(self, arguments); 1967 | self.off(type, wrapper); 1968 | } 1969 | this.on(type, wrapper); 1970 | return this; 1971 | }; 1972 | 1973 | ### 编写一个函数将列表子元素顺序反转 1974 | 1975 | ``` 1976 | 1982 | 1983 | 1993 | ``` 1994 | 1995 | ### 以下函数的作用是?空白区域应该填写什么 1996 | 1997 | ``` 1998 | // define 1999 | (function (window) { 2000 | function fn(str) { 2001 | this.str = str; 2002 | } 2003 | 2004 | fn.prototype.format = function () { 2005 | var arg = __1__; 2006 | return this.str.replace(__2__, function (a, b) { 2007 | return arg[b] || ''; 2008 | }); 2009 | }; 2010 | 2011 | window.fn = fn; 2012 | })(window); 2013 | 2014 | // use 2015 | (function () { 2016 | var t = new fn('

{1}{2}

'); 2017 | console.log(t.format('http://www.alibaba.com', 'Alibaba', 'Welcome')); 2018 | })(); 2019 | ``` 2020 | 2021 | define部分定义一个简单的模板类,使用{}作为转义标记,中间的数字表示替换目标,format实参用来替换模板内标记 2022 | 横线处填: 2023 | 2024 | 1. ``Array.prototype.slice.call(arguments, 0)`` 2025 | 2. ``/\{\s*(\d+)\s*\}/g`` 2026 | 2027 | ### 编写一个函数实现form的序列化(即将一个表单中的键值序列化为可提交的字符串) 2028 | 2029 | 2030 |
2031 | 2035 | 2040 | 2041 | 2042 | 2043 | 2044 | Football 2045 | Basketball 2046 | Female 2047 | Male 2048 |
2049 | 2050 | 2051 | 2120 | 2121 | ### 使用原生javascript给下面列表中的li节点绑定点击事件,点击时创建一个Object对象,兼容IE和标准浏览器 2122 | 2123 | ``` 2124 | 2130 | 2131 | Object: 2132 | { 2133 | "index": 1, 2134 | "name": "111", 2135 | "link": "http://1111" 2136 | } 2137 | ``` 2138 | 2139 | script: 2140 | 2141 | ``` 2142 | var EventUtil = { 2143 | getEvent: function (event) { 2144 | return event || window.event; 2145 | }, 2146 | getTarget: function (event) { 2147 | return event.target || event.srcElement; 2148 | }, 2149 | // 返回注册成功的监听器,IE中需要使用返回值来移除监听器 2150 | on: function (elem, type, handler) { 2151 | if (elem.addEventListener) { 2152 | elem.addEventListener(type, handler, false); 2153 | return handler; 2154 | } else if (elem.attachEvent) { 2155 | function wrapper(event) { 2156 | return handler.call(elem, event); 2157 | }; 2158 | elem.attachEvent('on' + type, wrapper); 2159 | return wrapper; 2160 | } 2161 | }, 2162 | off: function (elem, type, handler) { 2163 | if (elem.removeEventListener) { 2164 | elem.removeEventListener(type, handler, false); 2165 | } else if (elem.detachEvent) { 2166 | elem.detachEvent('on' + type, handler); 2167 | } 2168 | }, 2169 | preventDefault: function (event) { 2170 | if (event.preventDefault) { 2171 | event.preventDefault(); 2172 | } else if ('returnValue' in event) { 2173 | event.returnValue = false; 2174 | } 2175 | }, 2176 | stopPropagation: function (event) { 2177 | if (event.stopPropagation) { 2178 | event.stopPropagation(); 2179 | } else if ('cancelBubble' in event) { 2180 | event.cancelBubble = true; 2181 | } 2182 | } 2183 | }; 2184 | var DOMUtil = { 2185 | text: function (elem) { 2186 | if ('textContent' in elem) { 2187 | return elem.textContent; 2188 | } else if ('innerText' in elem) { 2189 | return elem.innerText; 2190 | } 2191 | }, 2192 | prop: function (elem, propName) { 2193 | return elem.getAttribute(propName); 2194 | } 2195 | }; 2196 | 2197 | var nav = document.getElementById('nav'); 2198 | 2199 | EventUtil.on(nav, 'click', function (event) { 2200 | var event = EventUtil.getEvent(event); 2201 | var target = EventUtil.getTarget(event); 2202 | 2203 | var children = this.children; 2204 | var i, len; 2205 | var anchor; 2206 | var obj = {}; 2207 | 2208 | for (i = 0, len = children.length; i < len; ++i) { 2209 | if (children[i] === target) { 2210 | obj.index = i + 1; 2211 | anchor = target.getElementsByTagName('a')[0]; 2212 | obj.name = DOMUtil.text(anchor); 2213 | obj.link = DOMUtil.prop(anchor, 'href'); 2214 | } 2215 | } 2216 | 2217 | alert('index: ' + obj.index + ' name: ' + obj.name + 2218 | ' link: ' + obj.link); 2219 | }); 2220 | ``` 2221 | 2222 | ### 有一个大数组,var a = ['1', '2', '3', ...];a的长度是100,内容填充随机整数的字符串.请先构造此数组a,然后设计一个算法将其内容去重 2223 | 2224 | ``` 2225 | /** 2226 | * 数组去重 2227 | **/ 2228 | function normalize(arr) { 2229 | if (arr && Array.isArray(arr)) { 2230 | var i, len, map = {}; 2231 | for (i = arr.length; i >= 0; --i) { 2232 | if (arr[i] in map) { 2233 | arr.splice(i, 1); 2234 | } else { 2235 | map[arr[i]] = true; 2236 | } 2237 | } 2238 | } 2239 | return arr; 2240 | } 2241 | 2242 | /** 2243 | * 用100个随机整数对应的字符串填充数组。 2244 | **/ 2245 | function fillArray(arr, start, end) { 2246 | start = start == undefined ? 1 : start; 2247 | end = end == undefined ? 100 : end; 2248 | 2249 | if (end <= start) { 2250 | end = start + 100; 2251 | } 2252 | 2253 | var width = end - start; 2254 | var i; 2255 | for (i = 100; i >= 1; --i) { 2256 | arr.push('' + (Math.floor(Math.random() * width) + start)); 2257 | } 2258 | return arr; 2259 | } 2260 | 2261 | var input = []; 2262 | fillArray(input, 1, 100); 2263 | input.sort(function (a, b) { 2264 | return a - b; 2265 | }); 2266 | console.log(input); 2267 | 2268 | normalize(input); 2269 | console.log(input); 2270 | ``` --------------------------------------------------------------------------------