熬夜吐血整理最全web前端面试题合辑(三)
JavaScript篇
Javascript很多考题都出自红宝书(JavaScript高级程序设计)
-
JS 有哪些数据类型?
基本数据类型:String Boolean Number Undefined Null
(本质上null就是一个占位用的对象)ES6新增了Symbol
(创建后独一无二且不可变的数据类型) - 引用数据类型:
Object
(狭义的对象
,Array
,Function
)
是否可以使用 typeof bar === 'object'
来检测bar
是不是object
类型,有何风险?
-
有风险。
typeof只能检测出
String,Number,Boolean,Undefined,Object,Function这六种类型.
typeof null返回的也是
Object,因为
null本质上就是一个占位的对象.另一方面,数组
Array也不能用
typeof检测数据类型,因为同样会返回
Object.
认清Array
的方法:
console.log( bar instanceof Array) // 如果数组,返回true
console.log( Array.isArray(bar)) //ES5方法
console.log( Object.prototype.toString.call(bar) === '[object Array]')
//可以判断的类型有 Number, String, Boolean, Undefined, Null,Object,Function,Array console.log( Object.prototype.toString.call(arr).slice(8, -1)); console.log( Object.prototype.toString.call(arr).slice(8, -1) === 'Array'])
什么是window
对象? 什么是document
对象?
window对象是指浏览器打开的窗口。
document对象是
Documentd对象(
HTML文档对象)的一个只读引用,
window对象的一个属性。
undefined
和null
的区别
undefined代表定义未赋值
null表示定义并且赋值了,只是值为null
介绍js有哪些内置对象?
-
数据封装类对象:
Object、Array、Boolean、Number和
String
Function、Arguments、Math、Date、RegExp、Error
内存溢出与内存泄露
-
内存溢出
-
一种程序运行出现的错误
-
占用的内存没有及时释放
new操作符具体干了什么呢?
1. 创建一个空对象,并且
this变量引用该对象,同时还继承了该函数的原型。
2. 属性和方法被加入到
this引用的对象中。
3. 新创建的对象由
this所引用,并且最后隐式的返回 this 。
var obj = {}; obj.__proto__ = Base.prototype; Base.call(obj);
对象的创建模式?
Object构造函数模式
/* 一个人: name:"Tom", age: 12 */ var p = new Object() //先创建空Object对象 p = {} //此时内部数据是不确定的 p.name = 'Tom'// 再动态添加属性/方法 p.age = 12 p.setName = function (name) { this.name = name } //测试 console.log(p.name, p.age)//Tom 12 p.setName('Bob') console.log(p.name, p.age)//Bob 12对象字面量模式
var p = { name: 'Tom', age: 12, setName: function (name) { this.name = name } } //测试 console.log(p.name, p.age) p.setName('JACK') console.log(p.name, p.age)
function createPerson(name, age) { //返回一个对象的函数===>工厂函数 var obj = { name: name, age: age, setName: function (name) { this.name = name } } return obj } // 创建2个人 var p1 = createPerson('Tom', 12) var p2 = createPerson('Bob', 13)
function Person(name, age) { this.name = name; this.age = age; this.setName = function(name){this.name=name;}; } new Person('tom', 12);
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.setName = function(name){this.name=name;}; new Person('tom', 12);
JavaScript原型,原型链 ? 有什么特点?
-
函数的
prototype属性(显示原型属性): 在定义函数时自动添加的, 默认值是一个空
Object实例对象(独独Object不满足)
console.log(Function.__proto__===Function.prototype)//true
console.log(Object.prototype.__proto__) // null
__proto__(隐式原型属性)在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
__proto__这条链向上查找, 找到返回
undefined
__proto__属性就形成了一个链的结构---->原型链
js引擎自动沿着这个原型链查找
Javascript如何实现继承?
-
1 构造继承 2 原型继承 3 实例继承 4 拷贝继承
//原型prototype机制或apply和call方法去实现较简单,建议使用构造函数与原型混合方式。 function Parent(){ this.name = 'wang'; } function Child(){ this.age = 28; } Child.prototype = new Parent();//继承了Parent,通过原型 var demo = new Child(); alert(demo.age); alert(demo.name);//得到被继承的属性
上下文作用域
全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节。
当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到,就会上溯到上层作用域查找,
直至全局函数,这种组织形式就是作用域链。
-
谈谈
我们每次调用函数时,解析器都会将一个上下文对象作为隐含的参数传递进函数。使用This(上下文)
对象的理解。this
来引用上下文对象,根据函数的调用形式不同,this
的值也不同。 this
的不同的情况:-
以函数的形式调用时,
this
是window
- 以方法的形式调用时,
this
就是调用方法的对象 - 以构造函数的形式调用时,
this
就是新创建的对象 - 使用
call
和apply
调用时,this
时指定的那个对象 - 在事件中,
this
指向触发这个事件的对象,特殊的是,IE
中的attachEvent
中的this
总是指向全局对象Window
;
call
和apply
的区别?
-
这两个方法都是函数对象的方法需要通过函数对象来调用
this
call是直接传递函数的实参而
apply需要将实参封装到一个数组中传递
Javascript
中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?
hasOwnProperty
什么是立即执行函数?使用立即执行函数的目的是什么?
-
概念:1. 声明一个匿名函数 2. 马上调用这个匿名函数
JS的语法
var liList = ul.getElementsByTagName('li') for(var i=0; i<6; i++){ function(j){ liList[j].onclick = function(){ alert(j) // 0、1、2、3、4、5 } }(i) } //在立即执行函数执行的时候,i 的值被赋值给 j,此后 j 的值一直不变。 //i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的j「分别」 //是 0、1、2、3、4、5。以上,就是立即执行函数的基本概念。
JSON
的了解?
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小.如:{"age":"12", "name":"back"}
JSON字符串转换为JSON对象: var obj =eval('('+ str +')'); var obj = str.parseJSON(); var obj = JSON.parse(str); JSON对象转换为JSON字符串: var last=obj.toJSONString(); var last=JSON.stringify(obj);
- NaN是什么?它是什么类型?如何检测一个变量是不是NaN?
答案:
NaN(not a Number)
,但是实际上它是Number
类型,typeof NaN
会返回number
.这个东西比较厉害,因为 NaN === NaN //false
- 你会发现它自己都不等于它自己,因此判断变量是否是它,不能使用
===
,可以使用isNaN()
方法
//检查变量是否是NaN isNaN(bar); Object.is(bar,NaN); //ES6方法,这个方法会修正JS中的一些小bug /* Object.is()方法,要求严格相等,且Object.is(NaN,NaN)会返回true */
这两个方法结合起来用的作用在于判断变量 bar 是一个 NaN非数值,但不是NaN本身这个数
/*补充一个*/ var a = b = 3; //实际上等同于 var a=b; b=3;
- 数组的filter,以下输出结果是什么(2018拼多多前端原题)
var arr = [1,2,3]; arr[10] = 9; arr.filter((item)=> { return item === undefined? }) //答案 []
解析: 是的,答案的确是[],不是[undefined x 7]。 首先,看下前两句执行后,arr是什么
console.log(arr) //[1,2,3, emptyx7, 9] console.log(arr[5]) //undefined
undefined和数组保留的
empty插槽并不是等同的,即使我们打印出相应的数据会显示
undefined,但是与js的
undefined是不同的,除了
arr.filter,包括
arr.map()函数都是会保留
empty插槽的。
- JS小数计算不准确的bug
console.log(0.1 + 0.2); console.log(0.1 + 0.2 == 0.3); //答案: 0.30000000000000004 false
解析: 详细的解析见连接,这里说一下解决办法 0.1+0.2 != 0.3
//解决办法 parseFloat((0.1+0.2).toFixed(10));
- 讨论实现判断变量是否是整数的函数isInter(x)的实现
答案: 在ES6中,是有现成的方法Number.isInteger可以使用的。如果自己实现,思路是什么呢
//1 异或运算 function isInter(x) { return x ^ 0 === x } //2 取整 return Math.round(x) === x //同样可以用floor ceil //取余 return (typeof x === 'number')&&(x % 1 === 0)
- 写一个sum方法,可以实现以下两种调用方式
console.log(sum(2,3)) //5 console.log(sum(2)(3)) //5
答案:
//方法1 var sum = function(x,y) { if(y === undefined) { return function(y) { return x + y; } }else { return x + y; } } //方法2 var sum = function(x){ if( arguments.length === 1) { return function (y) { return x + y; } } else { console.log('here'); return arguments[0] + arguments[1]; } }
- 递归设计。 实现一个函数,给该函数一个DOM节点,函数访问其所有子元素(所有子元素,不仅仅是直接子元素),每次访问子元素的时候,并为其传一个callback。
访问一个DOM tree,是一个经典的深度优先搜索的算法
function Traverse(DOM,callback) { callback(DOM); var list = DOM.children; Array.prototype.forEach.apply(list,(item)=>{ Traverse(item,callback); //递归 }) }
- Array数组的flat方法实现(2018网易雷火&伏羲前端秋招笔试)
Array的方法flat很多浏览器还未能实现,请写一个flat
方法,实现扁平化嵌套数组
,如:
Array var arr1 = [1, 2, [3, 4]]; arr1.flat(); // [1, 2, 3, 4]
这个问题的实现思路和Deep Clone非常相似,这里实现如下:
Array.prototype.flat = function() { var arr = []; this.forEach((item,idx) => { if(Array.isArray(item)) { arr = arr.concat(item.flat()); //递归去处理数组元素 } else { arr.push(item) //非数组直接push进去 } }) return arr; //递归出口 }
神秘力量的新解法
arr.prototype.flat = function() { this.toString().split(',').map(item=> +item ) }
1. toString方法,连接数组并返回一个字符串 '2,2,3,2,3,4' 2. split方法分割字符串,变成数组['2','2','3','2','3','4'] 3. map方法,将string映射成为number类型2,2,3,2,3,4
- 乱序的新定义
以前面试的时候遇到要写打乱数组的题目,直接就是一句代码搞定
arr.sort(() => Math.random() - 0.5);
后面意识到这种方法并不能真正意义上去实现打乱
这是参考别人实现的方法:sort()方法无用?
function shuffle(arr) { /* 即将它改造为一个对象, 原来的值存储在键v中,同时给它增加一个键r, 值为一个随机数,然后排序时比较这个随机数: */ let new_arr = arr.map(i => ({v: i, r: Math.random()})); new_arr.sort((a, b) => a.r - b.r); arr.splice(0, arr.length, ...new_arr.map(i => i.v)); return arr } /*这是测试代码*/ let a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']; console.log(shuffle(a));
-
js
延迟加载的方式有哪些?defer
(只支持IE
)和async
、创建script
,插入到DOM
中,加载完毕后callBack
(用得最多)、按需异步载入js
如何将浮点数点左边的数每三位添加一个逗号,如12000000.11
转化为『12,000,000.11』
?
(386485473.88).toLocaleString('en-US') // "386,485,473.88"
//方法2: var separator=(num)=>{ if(!num){ return '0.00'; }; let str = parseFloat(num).toFixed(2); return str && str .toString() .replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) { return $1 + ","; }); } separator(386485473.88) //"386,485,473.88"
列举ES6新特性
-
函数默认值、解构赋值 、模板字符串、
let/const、新增函数库(
Number String Array Obiect Math)、箭头函数
let
、const
、var
的区别?
let: 无变量提升、有块级作用域、不能重复声明
const:
-
无变量提升、有块级作用域、禁止重复声明
var存在变量提升,可以重复声明,赋值
箭头函数this
的指向
-
在箭头函数中,函数体内部没有自己的 this,默认在其内部调用 this 的时候,会自动查找其父级上下文的 this 对象(如果父级同样是箭头函数,则会按照作用域链继续向上查找)
手写es6
继承
promise的状态,链式调用,同步异步流程,唯一性
js
的 for
跟for in
循环它们之间的区别?
-
遍历数组时的异同:
for循环 数组下标的
typeof类型:
number,
-
for in循环数组下标的
typeof类型:
string
for循环 无法用于循环对象,获取不到
obj.length;
for in循环遍历对象的属性时,原型链上的所有属性都将被访问,
hasOwnProperty方法过滤或
Object.keys会返回自身可枚举属性组成的数组
js
对数组去重,列出你的思路(两种以上)
//第一种:原生方法去重,借助一个空数组实现去重,便于理解底层原理(unique函数带有参数) function unique(arr) { let a = []; //Array.prototype.forEach(function(item, index){}) : 遍历数组 arr.forEach((item,index)=>{ a.indexOf(item)===-1?a.push(item):''; }) return a; }; console.log(unique([1,3,3,5,7,1,1,3])); //[1,3,5,7]; //第二种: /*第二种同上(unique函数,但是不带参数) 拓展:需要注意的一点就是此函数可以传多个数组,但是要看arguments[index] 决定执行哪个数组,默认是执行第一个*/ function unique2() { let a = []; //Array.from(v) : 将伪数组对象或可遍历对象转换为真数组 Array.from(arguments[1]).forEach((item,index)=>{ a.indexOf(item)===-1?a.push(item):''; }) return a; } console.log(unique2([1,2,2,5,1,3,4,3],[1,1,5,55,4]);//[ 1, 5, 55, 4 ] //原理 function unique2() { let a = []; //Array.prototype 可以换成[] Array.prototype.forEach.call(arguments[0],(item,index)=>{ a.indexOf(item)===-1?a.push(item):''; }) return a; } console.log(unique2([1,2,2,5,1,3,4,3],[1,1,5,55,4]);//[[ 1, 2, 5, 3, 4 ]] //第三种方法: Array.from方法可以将Set结构转为数组 //new Set + Array.from function unique3(arr) { return Array.from(new Set(arr)); } console.log(unique3([1,2,2,5,1,3,4,3])); //第四种方法:最简单 let arr = [1,2,5,5,1,3,4,3,7,9,7]; let uniq = [...new Set(arr)]; console.log(uniq); //第五种方法: 基于数组原型链的去重方法 Array.prototype.uniq = function () { let a = []; this.forEach((item,index)=>{ a.indexOf(item)===-1?a.push(item):''; }) return a; } console.log([3,1,4,1,1,3].uniq());
什么是闭包?手写一个闭包
-
包含被引用变量(函数)的对象(通过
Chrome工具查看)
请将下列b函数进行修改,保证每次调用a都能+1(考闭包):
function b(){ var a=1; }; function b(){ var a=1; return ()=>{ a++; return a; } }; let c = b(); c(); //2 c(); //3 c(); //4
删除数组最后一个元素,改变原数组的方法?
myArr.pop()
myArr.slice(myArr.length-1)
myArr.splice(myArr.length,1)
列举常用的5个字符串操作方法
string.slice()可以从一个字符串中截取指定的内容,并将截取到内容返回,不会影响原变量
string.substring()
-
和slice()基本一致,不同的是它不能接受负值作为参数,如果设置一个负值,则会自动修正为0,
substring()中如果第二个参数小于第一个,自动调整位置
string.substr()
-
和slice()基本一致,不同的是它第二个参数不是索引,而是截取的数量
string.trim()去除前后空格
string.toUpperCase()将字符串转换为大写并返回
string.toLowerCase()将字符串转换为小写并返回
列举常用的5个数组操作方法
push()
pop()
shift()
unshift()
splice()
sort()
事件冒泡以及事件捕获。
如何实现数组的随机排序?
/*第一种方法*/ var arr = [1,2,3,4,5,6,7,8,9,10]; function randSort1(arr){ for(var i = 0,len = arr.length;i < len; i++ ){ var rand = parseInt(Math.random()*len); var temp = arr[rand]; arr[rand] = arr[i]; arr[i] = temp; } return arr; } console.log(randSort1(arr)); /*第二种方法*/ var arr = [1,2,3,4,5,6,7,8,9,10]; arr.sort(function(){ return Math.random() - 0.5; }) console.log(arr); /*第三种方法*/ var arr = [1,2,3,4,5,6,7,8,9,10]; function randSort2(arr){ var mixedArray = []; while(arr.length > 0){ var randomIndex = parseInt(Math.random()*arr.length); mixedArray.push(arr[randomIndex]); arr.splice(randomIndex, 1); } return mixedArray; } console.log(randSort2(arr));
- 排序方法
/*冒泡排序*/ function bubbleSort (myArr) { var len = myArr.length; var i,j,stop; for (i = 0; i < len; i++) { for (j = 0; stop = len-i, j < stop; j++) { if (myArr[j] > myArr[j+1]) { [myArr[j],myArr[j+1]] = [myArr[j+1],myArr[j]];//交换元素 } } } return myArr; } var res = bubbleSort([3,2,4,5,1]); console.log(res);//[1,2,3,4,5] /*选择排序*/ function selectSort (myArr) { var len = myArr.length, min; var i,j; for (i = 0; i < len; i++) { min = i; for (j=i+1; j < len;j++) { if (myArr[j]<myArr[min]) { min = j; } } //如果i不是最小的,则互换 if (i!= min) { [myArr[i],myArr[min]] = [myArr[min],myArr[i]]; } } return myArr; } var res = selectSort([3,2,4,5,1]); console.log(res);//[1,2,3,4,5] /*插入排序*/ function insertionSort(arr) { for(let i = 1; i < arr.length; i++) { for(let j = 0; j < i; j++) { if(arr[i] < arr[j]) { //在j位置新增一个元素arr[i] arr.splice(j, 0, arr[i]); //再把原来arr[i]所在的删除 arr.splice(i+1, 1); break } console.log(arr); // 至今不明白为什么控制台输出是[2,3,4,5,1] } } } var arr = [3,2,4,5,1]; insertionSort(arr); console.log(arr); //[1,2,3,4,5] /*归并排序*/ function mergeSort(arr) { var merge = function(leftArr, rightArr) { var resultArr = [] while(leftArr.length && rightArr.length) { resultArr.push(leftArr[0] <= rightArr[0] ? leftArr.shift() : rightArr.shift()); } return resultArr.concat(leftArr).concat(rightArr) } if(arr.length < 2) return arr; let mid = parseInt(arr.length/2) //取数组的中位下标,也可以用 arr.length >> 1 return merge(mergeSort(arr.slice(0, mid)), mergeSort(arr.slice(mid))); } var res = mergeSort([3,2,4,5,1]); console.log(res);//[1,2,3,4,5] /*快速排序*/ function quickSort(arr) { if (arr.length <= 1) { return arr //递归的出口 } var left = [], right = [], current = arr.splice(0,1); //此时数组少了一个数(第一个) for (let i =0; i<arr.length; i++) { if (arr[i] < current) { left.push(arr[i]); // 放左边 }else { right.push(arr[i]); //放右边 } } return quickSort(left).concat(current,quickSort(right)); } /*测试*/ let res = quickSort([4,7,21,6,9,3,1]); console.log(res);//[1,3,4,6,7,9,21]
- DOM操作——怎样添加、移除、移动、复制、创建和查找节点?
这个面试的时候问了怎么用js动态控制插入节点
(1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
(2)添加、移除、替换、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子节点前插入一个新的子节点
(3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
getElementById() //通过元素Id,唯一性
Promise
怎么使用?
-ES6
的Promise
是一个构造函数,用来生成promise
实例。 作用: 解决回调地狱(回调函数的层层嵌套, 编码是不断向右扩展, 阅读性很差)- 能以同步编码的方式实现异步调用
Promise:
-
创建
promise对象
let promise = new Promise((resolve, reject) => { //初始化promise状态为 pending //执行异步操作 if(异步操作成功) { resolve(value);//修改promise的状态为fullfilled } else { reject(errMsg);//修改promise的状态为rejected } })
promise的
then()
promise.then(function( result => console.log(result), errorMsg => alert(errorMsg) )); ---- --- --- ----- ------------------------- promise.then(()=>{},()=>{}).then()... //使用promise封装处理ajax请求 let request = new XMLHttpRequest(); request.onreadystatechange = function () { } request.responseType = 'json'; request.open("GET", url); request.send();
async/await语法了解吗?目的是什么
概念:真正意义上去解决异步回调的问题,同步流程表达异步操作(本质是
Generator的语法糖)
* 不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行 * 返回的总是Promise对象,可以用then方法进行下一步操作 * async取代Generator函数的星号*,await取代Generator的yield * 语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
- 如何用正则实现 string.trim() //去除字符串前后的空格
function trim(string){ return string.replace(/^\s+|\s+$/g, '')}
// event(事件)工具集,来源:github.com/markyun markyun.Event = { // 页面加载完成后 readyEvent : function(fn) { if (fn==null) { fn=document; } var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = fn; } else { window.onload = function() { oldonload(); fn(); }; } }, // 视能力分别使用dom0||dom2||IE方式 来绑定事件 // 参数: 操作的元素,事件名称 ,事件处理程序 addEvent : function(element, type, handler) { if (element.addEventListener) { //事件类型、需要执行的函数、是否捕捉 element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + type, function() { handler.call(element); }); } else { element['on' + type] = handler; } }, // 移除事件 removeEvent : function(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.datachEvent) { element.detachEvent('on' + type, handler); } else { element['on' + type] = null; } }, // 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获) stopPropagation : function(ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, // 取消事件的默认行为 preventDefault : function(event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, // 获取事件目标 getTarget : function(event) { return event.target || event.srcElement; }, // 获取event对象的引用,取到事件的所有信息,确保随时能使用event; getEvent : function(e) { var ev = e || window.event; if (!ev) { var c = this.getEvent.caller; while (c) { ev = c.arguments[0]; if (ev && Event == ev.constructor) { break; } c = c.caller; } } return ev; } };
- 刁钻代码题
a.x = a = {}
问 a.x 是多少?undefined
var a = {n:1}; var b = a; a.x = a = {n:2};
-
(a ==1 && a== 2 && a==3)
可能为true
吗?a = { value: 0, toString(){ a.value += 1 return a.value } }
-
["1", "2", "3"].map(parseInt)
答案是多少?详细解析parseInt()
函数能解析一个字符串,并返回一个整数,需要两个参数 (val
,radix
),其中radix
表示要解析的数字的基数。【该值介于2
~36
之间,并且字符串中的数字不能大于radix
才能正确返回数字结果值】; - 但此处
map
传了3
个 (element
,index
,array
),我们重写parseInt
函数测试一下是否符合上面的规则。function parseInt(str, radix) { return str+'-'+radix; }; var a=["1", "2", "3"]; a.map(parseInt); // ["1-0", "2-1", "3-2"] 不能大于radix /* 因为二进制里面,没有数字3,导致出现超范围的radix赋值和不合法的进制解析, 才会返回NaN所以["1", "2", "3"].map(parseInt) 答案也就是:[1, NaN, NaN]*/
以下两个函数是否等价
function foo1() { return { bar: "hello" }; } function foo2() { return { bar: "hello" }; } console.log(foo1()); // {bar : "hellp"} console.log(foo2()); // undefined
答案:不等价!! 注意return 后面大括号的位置,第二个函数js会默认return 后面返回的东西(是空),等价于 return undefined {xxx} //后面当然,当然是写了也白写
-
事件是?IE与火狐的事件机制有什么区别? 如何阻止冒泡?
1. 我们在网页中的某个操作(有的操作对应多个事件)。例如:当我们点击一个按钮就会产生一个事件。是可以被JavaScript
侦测到的行为。
2. 事件处理机制:IE
是事件冒泡、Firefox
同时支持两种事件模型,也就是:捕获型事件和冒泡型事件;
3.ev.stopPropagation();
(旧ie
的方法ev.cancelBubble = true;
) -
如何实现深拷贝?
理想方案:(但不能处理函数数据)function clone(object){ let _obj=JSON.parse(JSON.stringify(obj))}
function clone(obj){ if(!obj&& typeof obj!== 'object'){ return; } var newObj=obj.constructor===Object?{}:[]; for(var key in obj){ newObj[key] =(obj[key]&&typeof obj[key]==='object')?clone(obj[key]):obj[key]; } return newObj; }
你对重绘、重排的理解?
-
首先网页数次渲染生成时,这个可称为重排;
DOM、样式表、用户事件或行为(鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)这些都会导致页面重新渲染,那么重新渲染,就需要重新生成布局和重新绘制节点,前者叫做"重排",后者"重绘";
项目上线前,能做哪些优化?
-
图片预加载,
css样式表放在顶部且link链式引入,
javascript放在底部
body结束标签前;
dns-prefetch对项目中用到的域名进行
DNS预解析,减少
DNS查询,如:
<link rel="dns-prefetch" href="//github.com"/>;
CDN托管;
API接口数据设置缓存,
CSS Sprites/SVG Sprites,
JS、
CSS源码压缩、图片大小控制合适,使用
iconfont(字体图标)或
SVG,它们比图片更小更清晰,网页
Gzip压缩;
DOM操作次数,优化
javascript性能;
DOM元素数量,合理利用
:after、:before等伪类;
CSS Expression(
css表达式)又称
Dynamic properties(动态属性)
cookie问题;
iframe使用,它会阻塞主页面的渲染;
JavaScript、
CSS、
字体、
图片等,甚至
html;
DOM 清空子元素的方法
<!--调用函数 删除--> <input type="button" onclick="deleteChildren('#bbb');" value="删除所有子元素" />
function deleteChildren (a) { //获取父元素 var parentNode = document.querySelector(a); //判断是否包含子元素 if (parentNode.hasChildNodes()) { var len = parentNode.childNodes.length;//子元素的个数 for (var i = 0; i < len; i++) { parentNode.removeChild(parentNode.childNodes[0]);//从第一个元素开始删除 } } }阅读更多
- 前端面试】前端面试题300道~~熬夜吐血整理
- 2016最全的web前端面试题及答案整理
- 【web前端面试题整理04】阿里一行之大神面对面
- 【web前端面试题整理06】成都第一弹,邂逅聚美优品
- 【web前端面试题整理07】我不理解表现与数据分离。。。
- WEB前端面试题整理
- WEB前端面试题汇总整理02
- 【web前端面试题整理05】做几道前端面试题休息休息吧
- web前端面试题整理(HTML篇)
- web前端面试题整理(HTML篇)
- 【web前端面试题整理03】来看一点CSS相关的吧
- 【web前端面试题整理03】来看一点CSS相关的吧 - 叶小钗
- 【web前端面试题整理02】前端面试题第二弹袭来,接招!
- WEB前端面试题整理
- 【web前端面试题整理04】阿里一行之大神面对面
- 最全web前端资料整理
- WEB前端面试题查询整理
- WEB前端面试题查询整理
- web前端面试题整理后篇(程序篇)
- 【web前端面试题整理07】我不理解表现与数据分离。。。