CSS
列举不同的清除浮动的技巧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* 1.添加新元素 */ <div class="outer"> <div class="div1"></div> <div class="div2"></div> <div class="div3"></div> <div class="clearfix"></div> </div> .clearfix { clear: both; } /* 2.为父元素增加样式 */ .clearfix { overflow: auto; zoom: 1; // 处理兼容性 } /* 3.:after 伪元素方法 (作用于父元素) */ .outer { zoom: 1; &:after { display: block; height: 0; clear: both; content: '.'; visibillity: hidden; } } |
一像素边框
使用sass
语法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /* 定义 */ @mixin border-1px ($color) { position: relative; &:after { display: block; position: absolute; left: 0; bottom: 0; width: 100%; border-top: 1px solid $color; context: ''; } } @media (-webkit-min-device-pixel-radio: 1.5), (min-device-pixel-radio: 1.5) { border-1px { &:after { -webkit-transform: scaleY(0.7); transform: scaleY(0.7); } } } @media (-webkit-min-device-pixel-radio: 2), (min-device-pixel-radio: 2) { border-1px { &:after { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); } } } /* 使用方式 */ @inclue border-1px(rgba(7, 17, 27, .1)); |
形成BFC(Block Formatting Context)的几种方式
BFC全称”Block Formatting Context”, 中文为“块级格式化上下文”。BFC元素特性表现原则就是,内部子元素再怎么翻江倒海,翻云覆雨都不会影响外部的元素。
float
为left|right
overflow
为hidden|auto|scroll
display
为table-cell|table-caption|inline-block
position
为absolute|fixed
布局
-
左定宽右自适应宽度,并且等高布局(最小高度200px)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* HTML */ <div class="container"> <div class="left">Left silder</div> <div class="content">Main content</div> </div> /* CSS */ .container { overflow: hidden; } .left { float: left; width: 200px; margin-bottom: -9999px; padding-bottom: 9999px; background-color: #eee; } .content { margin-left: 200px; margin-bottom: -9999px; padding-bottom: 9999px; background-color: #ccc; } .left, .content { min-height: 200px; height: auto !important; } |
JS
async
与defer
区别
异步(async) 脚本将在其加载完成后立即执行,而 延迟(defer) 脚本将等待 HTML 解析完成后,并按加载顺序执行。
location.replace()
与location.asign()
区别
location.replace()
的url不会出现在history中
new
操作符
- 创建一个空对象,并且
this
变量引用该对象,同时还继承了 该函数的原型 - 属性和方法被加入到
this
引用的对象中 - 新创建的对象由
this
所引用,并且最后隐式的返回this
AMD CMD CommonJS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* AMD是RequireJS对模块化的定义 * CMD是seaJS对模块化的定义 * CommonJS是Node对模块化的规范 **/ /* AMD 依赖关系前置 */ define(['./a', './b'], function (a, b) { a.something(); b.something(); }) /* CMD 按需加载,依赖就近 */ define(function (require, exports, module) { var a = require('./a'); a.something(); var b = require('./b'); b.something(); }) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 创建节点 createDocumentFragment() createElement() createTextNode() // 添加 移除 替换 插入 appendChild() removeChild() replaceChild() insertBefore() // 查找 getElementsByTagName() getElementsByName() getElementsByClassName() getElementById() querySelector() querySelectorAll() |
JS设置css样式的几种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* 1.直接设置style属性 */ element.style.height = '100px'; /* 2.直接设置属性 */ element.setAttribute('height', '100px'); /* 3.使用setAttribute设置style属性 */ element.setAttribute('style', 'height: 100px !important'); /* 4.使用setProperty设置属性,通过第三个参数设置important */ element.style.setProperty('height', '300px', 'important'); /* 5.设置cssText */ element.style.cssText += 'height: 100px !important'; |
阻止默认行为
1 2 3 4 5 6 7 8 9 10 | function stopDefault( e ) { // 阻止默认浏览器动作(W3C) if ( e && e.preventDefault ) { e.preventDefault(); } else { // IE中阻止函数器默认动作的方式 window.event.returnValue = false; } return false; } |
阻止冒泡
1 2 3 4 5 6 7 8 9 10 | function stopBubble(e) { // 如果提供了事件对象,则这是一个非IE浏览器 if ( e && e.stopPropagation ) { // 因此它支持W3C的stopPropagation()方法 e.stopPropagation(); } else { // 否则,我们需要使用IE的方式来取消事件冒泡 window.event.cancelBubble = true; } } |
Ajax交互过程
- 创建XMLHttpRequest对象,也就是创建一个异步调用对象.
- 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
- 设置响应HTTP请求状态变化的函数.
- 发送HTTP请求.
- 获取异步调用返回的数据.
- 使用JavaScript和DOM实现局部刷新.
考察知识点最广的JS面试题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function Foo() { getName = function () { alert(1); } return this; } Foo.getName = function () { alert(2); } Foo.prototype.getName = function () { alert(3); } var getName = function () { alert(4); } function getName () { alert(5); } /* 写出输出 */ Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName(); |
具体讲解参见一道常被人轻视的前端JS面试题
JS数组深浅拷贝
slice
实现
1 2 3 4 5 6 7 8 | var arr = ['old', 1, true, null, undefined]; var new_arr = arr.slice(); new_arr[0] = 'new'; console.log(arr) // ["old", 1, true, null, undefined] console.log(new_arr) // ["new", 1, true, null, undefined] |
concat
实现
1 2 3 4 5 6 7 8 | var arr = ['old', 1, true, null, undefined]; var new_arr = arr.concat(); new_arr[0] = 'new'; console.log(arr) // ["old", 1, true, null, undefined] console.log(new_arr) // ["new", 1, true, null, undefined] |
以上两种方法只是浅拷贝,如果数组元素是基本类型,就会拷贝一份新的;但是如果数组元素是对象或者数组,就只会拷贝引用(类似指针),修改其中一个就会影响另外一个。
1 2 3 4 5 6 7 8 9 | var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]; var new_arr = arr.concat(); new_arr[0] = 'new'; new_arr[3][0] = 'new1'; console.log(arr) // ["old", 1, true, ['new1', 'old2'], {old: 1}] console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}] |
JSON.stringify
实现数组深拷贝
1 2 3 4 5 6 7 8 9 | var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]; var new_arr = JSON.parse(JSON.stringify(arr)); new_arr[0] = 'new'; new_arr[3][0] = 'new1'; console.log(arr) // ["old", 1, true, ['old1', 'old2'], {old: 1}] console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}] |
简单粗暴,但是问题是不能拷贝函数,不推荐。
然后我们来手动实现深浅拷贝。
- 浅拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 | var shallowCopy = function (obj) { // 判断是否是数组或者对象 if (typeof obj !== 'object') { return } var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; } |
- 深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 | var deepCopy = function (obj) { if (typeof obj !== 'object') { return } var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj } |
数组去重
filter
+indexOf
1 2 3 4 5 6 | function unique (arr) { var res = arr.filter(function (item, index, array) { return array.indexOf(item) === index; }) return res; } |
filter
+sort
1 2 3 4 5 | function unique (arr) { return arr.concat().sort().filter(function (item, index, array) { return !index || item !== array[index - 1]; }) } |
ES6
1 2 3 | function uniqu3 (arr) { return [... new Set(arr)]; } |
找出数组中的最大值
reduce
1 2 3 4 5 6 7 | var arr = [6, 4, 1, 8, 2, 11, 3]; function max (prev, next) { return Math.max(prev, next) } console.log(arr.reduce(max)); |
apply
1 2 3 | var arr = [6, 4, 1, 8, 2, 11, 3]; console.log(Math.max.apply(null, arr)); |
ES6
1 2 3 4 5 6 7 | var arr = [6, 4, 1, 8, 2, 11, 3]; function max (arr) { return Math.max(...arr); } console.log(max(arr)); |
打乱数组的方法
1 2 3 4 5 6 7 8 | var arr = []; for (var i = 0; i < 100; i++) { arr[i] = i; } arr.sort(function () { return 0.5 - Math.random(); }); |
数组扁平化
1 2 3 4 5 6 7 8 9 10 11 12 | var arr = [1, [2, [3, 4]]]; function flatten(arr) { while (arr.some(item => Array.isArray(item))) { arr = [].concat(...arr); } return arr; } console.log(flatten(arr)) |
排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // 冒泡 function bubbleSort2(arr) { var len = arr.length; for (var i = 0; i <= len - 1; i++) { for (var j = 0; j <= len - i; j++) { if (arr[j + 1] < arr[j]) { var temp; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; } // 快速排序 function quickSort(arr) { if(arr.length == 0) { return []; // 返回空数组 } var cIndex = Math.floor(arr.length / 2); var c = arr.splice(cIndex, 1); var l = []; var r = []; for (var i = 0; i < arr.length; i++) { if(arr[i] < c) { l.push(arr[i]); } else { r.push(arr[i]); } } return quickSort(l).concat(c, quickSort(r)); } |
数字格式化 1234567890 -> 1,234,567,890
1 2 3 4 5 6 | function formatNum (num) { return num.replace(/\B(?=(\d{3})+(?!\d))/g, ','); } var num = '1234567890'; var res = formatNum(num); console.log(res); |
打乱数组的方法
1 2 3 4 5 6 7 8 | var arr = []; for (var i = 0; i < 100; i++) { arr[i] = i; } arr.sort(function () { return 0.5 - Math.random(); }) |
尾调用优化
即只保存内层函数的调用帧(只有开启严格模式,才会生效),只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则无法进行“尾调用优化。
1 2 3 4 5 6 7 8 9 10 11 12 | function factorial(n) { if (n === 1) return 1; return n * factorial(n-1); } factorial(5) /* 优化尾递归 */ function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) |
柯里化
实现add(1,2)
和add(1)(2)
均输出3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function add () { let sum = 0; Array.prototype.forEach.call(arguments, function (item, index){ if (typeof item !== 'number') { return false; } else { sum += item; } }) var tmp = function () { Array.prototype.forEach.call(arguments, function (item, index){ if (typeof item !== 'number') { return false; } else { sum += item; } }) return tmp; } tmp.toString = function () { return sum } return tmp; } add(1, 2); // 3 add(1)(2); // 3 add(1, 2, 3)(1, 4)(2, 2)(1) // 16 |
ES8 新特性
- 字符串填充
1 2 | str.padStart(targetLength [, padString]) str.padEnd(targetLength [, padString]) |
values
和entries
函数
1 2 | Object.values(obj) Object.entries(obj) |
getOwnPropertyDescriptors
函数
1 | Object.getOwnPropertyDescriptors(obj) |
- 函数参数逗号结尾
1 | function es8(var1, var2, var3,) {} |
- 异步函数
由async
关键词定义的函数声明了一个可以异步执行的函数,返回一个AsyncFunction
类型的对象。
1 2 3 4 5 6 7 8 9 10 11 12 | fucntion fetchTextByPromise () { return new Promise(resolve => { setTimeout(() => { resolve('es8'); }, 2000); }); } async function sayHello () { const externalFetchedText = await fetchTextByPromise(); console.log(`Hello, ${externalFetchedText}`); } sayHello(); |
数据类型判断
1 2 3 4 5 6 7 8 9 10 | var class2type = {}; 'Boolean Number String Function Array Date RegExp Object Error Null Undefined'.split(' ').map((item, index) => { class2type['[object ' + item + ']'] = item.toLowerCase(); }) function type (obj) { return typeof obj === 'object' || typeof obj === 'function' ? class2type[{}.toString.call(obj)] || 'object' : typeof obj; } |
防抖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* * func:需要调用的函数 * wait: 防抖时间 * immediate:布尔值,是否立即执行 **/ var debounce = function (func, wait, immediate) { var timeout; return function () { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { // 是否立即执行func var callNow = !timeout; timeout = setTimeout(function () { timeout = null; }, wait); if (callNow) { func.apply(context, args); } } else { timeout = setTimeout(function () { func.apply(context, args); }, wait); } } } |
简单的字符串模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var TemplateEngine = function(tpl, data) { var re = /<%([^%>]+)?%>/g, match; while(match = re.exec(tpl)) { tpl = tpl.replace(match[0], data[match[1]]) } return tpl; } var template = '<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>'; console.log(TemplateEngine(template, { name: "Yeaseon", age: 24 })); // '<p>Hello, my name is Yeaseon. I\'m 24 years old.</p>'; |
apply
、call
和bind
apply
和call
在严格模式下,未指定环境对象而调用函数,则this 值不会转型为window。除非明确把函数添加到某个对象或者调用apply()或call(),否则this 值将是undefined。
在非严格模式下,call、apply的第一个参数传递为null或undefined时,函数体内的this会指向默认的宿主对象,在浏览器中则是window。
apply
、call
和bind
比较
1 2 3 4 5 6 7 8 9 10 11 12 13 | var obj = { x: 81 }; var foo = { getX: function () { return this.x; } } console.log(foo.getX.bind(obj)()); console.log(foo.getX.apply(obj)); console.log(foo.getX.call(obj)); |
很明显,bind
方法后面多了一对括号。也就是说,当你希望改变上下文环境之后并且立即执行,而是回调执行的时候(多用于事件监听器函数),使用bind()
方法,而apply/call
则会立即执行函数。
- 定义一个 log 方法,让它可以代理 console.log 方法。
1 2 3 4 5 | function log(){ console.log.apply(console, arguments); }; log(1); //1 log(1,2); //1 2 |
- 给每一个 log 消息添加一个”(app)”的前辍,比如:
1 | log("hello world"); //(app)hello world |
1 2 3 4 5 | function log(){ var args = Array.prototype.slice.call(arguments); args.unshift('(app)'); console.log.apply(console, args); }; |
apply
实现bind
1 2 3 4 5 | function bind (fn, context) { return function () { return fn.apply(context, argument); } } |
创建对象
- 工厂模式
1 2 3 4 5 6 7 8 9 10 11 12 | function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor"); |
- 构造函数模式
1 2 3 4 5 6 7 8 9 10 | function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); |
- 原型模式
1 2 3 4 5 6 7 8 9 10 | function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //"Nicholas" |
- 构造函数 + 原型模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true |
JS实现Jquery的extend()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | function extend() { // 默认不进行深拷贝 var deep = false; var name, options, src, copy, clone, copyIsArray; var length = arguments.length; // 记录要复制的对象的下标 var i = 1; // 第一个参数不传布尔值的情况下,target 默认是第一个参数 var target = arguments[0] || {}; // 如果第一个参数是布尔值,第二个参数是 target if (typeof target == 'boolean') { deep = target; target = arguments[i] || {}; i++; } // 如果target不是对象,我们是无法进行复制的,所以设为 {} if (typeof target !== "object" && !isFunction(target)) { target = {}; } // 循环遍历要复制的对象们 for (; i < length; i++) { // 获取当前对象 options = arguments[i]; // 要求不能为空 避免 extend(a,,b) 这种情况 if (options != null) { for (name in options) { // 目标属性值 src = target[name]; // 要复制的对象的属性值 copy = options[name]; // 解决循环引用 if (target === copy) { continue; } // 要递归的对象必须是 plainObject 或者数组 if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { // 要复制的对象属性值类型需要与目标属性值相同 if (copyIsArray) { copyIsArray = false; clone = src && Array.isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; } target[name] = extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } } return target; }; |
自定义事件(通过观察者模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | function EventTarget () { this.handlers = {}; } EventTarget.prototype = { constructor: EventTarget, addHandler: function (type, handler) { if (typeof this.handlers[type] == 'undefined') { this.handlers[type] = []; } this.handlers[type].push(handler) }, fire: function (event) { if (!event.target) { event.target = this; } if (this.handlers[event.type] instanceof Array) { var handlers = this.handlers[event.type]; for (var i = 0, len = handlers.length; i < len; i++) { handlers[i](event); } } }, removeHandler: function (type, handler) { if (this.handlers[type] instanceof Array) { var handlers = this.handlers[type]; for (var i = 0, len = handlers.length; i < len; i++) { if (handlers[i] === handler) { break; } } handlers.splice(i, 1); } } } |
安全
跨域的几种方法
- 主域相同的跨域
1 | document.domain |
-
window.postMessage
-
JSONP
跨域(只支持GET
)
1 2 3 4 5 6 7 8 9 10 11 | function todo(data){ console.log('The author is: '+ data.name); } var script = document.createElement('script'); /* 向服务器www.yeaseonzhang.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字。 */ script.src = 'http://www.yeaseonzhang.com/author?callback=todo'; document.body.appendChild(script); /* 服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。 */ todo({"name": "fewjq"}); /* 由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了todo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象。*/ |
websocket
跨域
XSS 和 CSRF
性能
CSS 优化
- 正确的时机调用CSS
- 使用媒体查询标记
<link>
,选择性加载 - 减少css文件数量
- 压缩css代码