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如何实现继承?
//原型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
什么是立即执行函数?使用立即执行函数的目的是什么?
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(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;
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
插槽的。
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));
//1 异或运算
function isInter(x) {
return x ^ 0 === x
}
//2 取整
return Math.round(x) === x //同样可以用floor ceil
//取余
return (typeof x === 'number')&&(x % 1 === 0)
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];
}
}
function Traverse(DOM,callback) {
callback(DOM);
var list = DOM.children;
Array.prototype.forEach.apply(list,(item)=>{
Traverse(item,callback); //递归
})
}
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
的指向
手写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()
string.substr()
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]
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子节点前插入一个新的子节点
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
* 语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
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
查询,如:
;CDN
托管;API
接口数据设置缓存,CSS Sprites/SVG Sprites
, JS
、CSS
源码压缩、图片大小控制合适,使用iconfont
(字体图标)或SVG
,它们比图片更小更清晰,网页Gzip
压缩;DOM
操作次数,优化javascrip
t性能;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]);//从第一个元素开始删除
}
}
}
参考文章在这