咱们在面试的时候,常常会被面试官问到几个手写代码的问题。手写一个数组去重复,手写一个深拷贝,手写一个数组拍平等等。有些小伙伴面试以前准备了,可是在手写的时候仍是会忘掉,本文总结了一个如何应对手写代码的大纲,前面是一些基础内容,可是也但愿能帮助到你,都会就当复习一遍。javascript
做者简介:koala,专一完整的 Node.js 技术栈分享,从 JavaScript 到 Node.js,再到后端数据库,祝您成为优秀的高级 Node.js 工程师。【程序员成长指北】做者,Github 博客开源项目 github.com/koala-codin…java
手写函数的时候,我每次给参数或者函数起名字都要犹豫那么几秒。面试官还觉得我不会写嘞!node
arr
和 arr+功能名称
简写。其实直接可使用 arr1,arr2 来代替,能区分开就行了,别在犹豫这种事浪费时间。git
看完一道手写面试题,能够先看下是否须要声明新变量,须要返回的结果和结果类型等。程序员
for (var num =1; num<=10; num++) {
document.write(num+" <br />"); //1 2 3 4 5 6 7 8 9 10
}
复制代码
for-in 循环主要用于遍历对象 for() 中的格式:for(keys in object){} keys 表示 obj 对象的每个键值对的键!!全部循环中,须要使用obj[keys]来取到每个值!!!es6
for-in 循环,遍历时不只能读取对象自身上面的成员属性,也能延续原型链遍历出对象的原型属性 因此,可使用 hasOwnProperty 判断一个属性是否是对象自身上的属性github
obj.hasOwnProperty(keys)==true 表示这个属性是对象的成员属性,而不是原先属性。面试
//声明一个Peson类
function Person(){
this.name = "kaola";
this.age = 24;
this.func1 = function(){
}
}
//实例化这个类
var bei = new Person();
//使用for-in遍历这个对象
for(keys in bei){
console.log(bei[keys])
}
复制代码
ES6 借鉴 C++、Java、C# 和 Python 语言,引入了 for...of 循环,做为遍历全部数据结构的统一的方法。数据库
一个数据结构只要部署了 Symbol.iterator 属性,就被视为具备 iterator 接口,就能够用 for...of 循环遍历它的成员。也就是说,for...of 循环内部调用的是数据结构的 Symbol.iterator 方法。json
for...of 循环可使用的范围包括数组、Set 和 Map 结构、某些相似数组的对象(好比arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。
关于递归的详细内容能够看个人这篇文章,文章中经常使用的递归手写代码都用写到,
如何判断数据类型?怎么判断一个值究竟是数组类型仍是对象?
三种方式,分别为 typeof
、instanceof
和Object.prototype.toString.call()
经过 typeof
操做符来判断一个值属于哪一种基本类型。
typeof 'seymoe' // 'string'
typeof true // 'boolean'
typeof 10 // 'number'
typeof Symbol() // 'symbol'
typeof null // 'object' 没法断定是否为 null
typeof undefined // 'undefined'
typeof {} // 'object'
typeof [] // 'object'
typeof(() => {}) // 'function'
复制代码
上面代码的输出结果能够看出,
null
的断定有偏差,获得的结果 若是使用 typeof null
获得的结果是object
操做符对对象类型及其子类型,例如函数(可调用对象)、数组(有序索引对象)等进行断定,则除了函数都会获得 object
的结果。
在对象的子类型和null
状况下,能够看出typeOf
对于判断类型还有一些不足。
经过 instanceof
操做符也能够对对象类型进行断定,其原理就是测试构造函数的prototype
是否出如今被检测对象的原型链上。
[] instanceof Array // true
({}) instanceof Object // true
(()=>{}) instanceof Function // true
复制代码
复制代码注意:instanceof
也不是万能的。
举个例子:
let arr = []
let obj = {}
arr instanceof Array // true
arr instanceof Object // true
obj instanceof Object // true
obj instanceof Array // false
复制代码
在这个例子中,arr
数组至关于 new Array()
出的一个实例,因此 arr.__proto__ === Array.prototype
,又由于 Array
属于 Object
子类型,即Array.prototype.__proto__ === Object.prototype
,所以 Object
构造函数在 arr
的原型链上。因此 instanceof
仍然没法优雅的判断一个值到底属于数组仍是普通对象。
还有一点须要说明下,有些开发者会说 Object.prototype.__proto__ === null
,岂不是说 arr instanceof null
也应该为 true
,这个语句其实会报错提示右侧参数应该为对象,这也印证 typeof null
的结果为 object
真的只是javascript
中的一个bug
。
Object.prototype.toString()
(推荐款)能够说是断定 JavaScript
中数据类型的终极解决方法了,具体用法请看如下代码:
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call(() => {}) // '[object Function]'
Object.prototype.toString.call('seymoe') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(Symbol()) // '[object Symbol]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(Math) // '[object Math]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new WeakSet()) // '[object WeakSet]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new WeakMap()) // '[object WeakMap]'
复制代码
咱们能够发现该方法在传入任何类型的值都能返回对应准确的对象类型。用法虽简单明了,但其中有几个点须要理解清楚:
Object.prototype.toString()
方法获得对象内部属性 [[Class]]
null
和 undefined
可以输出结果是内部实现有作处理对于类型判断,咱们能够经过 Object.prototype.toString() 进行一个简单的封装,这样咱们再判断类型的时候,直接使用 type 函数就能够了。
代码以下:
var type = function(data) {
var toString = Object.prototype.toString;
var dataType = toString
.call(data)
.replace(/\[object\s(.+)\]/, "$1")
.toLowerCase()
return dataType
};
复制代码
Array.isArray 也能够判断传递参数是不是数组。 须要注意的是这是 Array.isArray
是 ES 5.1
推出的,不支持 IE6~8
,因此在使用的时候也应注意兼容问题。
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
复制代码
if([]) {
console.log('true');
} else {
consoel.log('false');
}
// res:true
if({}) {
console.log('true');
} else {
consoel.log('false');
}
// res:true
复制代码
看这段代码,不知道有没有小伙伴会认为输出false,以上对象和数组虽然为空,可是会被转换为ture,因此在写一些判断条件时候要格外注意。
数组为空很简单,经过上面的类型判断是数组类型,而后它的length>0
便可
Object.getOwnPropertyNames()
使用Object.getOwnPropertyNames()
。返回值是对象中属性明名成的数组
var obj = {}
Object.getOwnPropertyNames(obj).length === 0; // true
复制代码
json 对象转换为字符串
将json对象转换为字符串,而后比较该字符串与"{}"是否相等
var obj = {};
var a = JSON.stringify(obj);
a === "{}"; // true
// a === {}; // false
复制代码
for...in... 循环判断
var obj = {};
function judgeObj(obj) {
for(let key in obj) {
return false
}
return true
};
judgeObj(obj); // true
复制代码
Object.keys()
使用Object.keys()。ES6的新方法,返回值一样是属性名组成的数组
var obj = {}
Object.keys(obj).length === 0; // true
复制代码
直接使用对象属性判断 前提是要肯定若是obj不为空,必定会包含name属性
var obj = {};
obj && obj.name ? '不为空' : '为空'; //
复制代码
比较过程:
(1)若是两个值类型相同,再进行三个等号(===)的比较
(2)若是两个值类型不一样,也有可能相等,需根据如下规则进行类型转换在比较:
1)若是一个是null,一个是undefined,那么相等
2)若是一个是字符串,一个是数值,把字符串转换成数值以后再进行比较
(1)若是类型不一样,就必定不相等
(2)若是两个都是数值,而且是同一个值,那么相等;若是其中至少一个是NaN,那么不相等。(判断一个值是不是NaN,只能使用isNaN( ) 来判断)
(3)若是两个都是字符串,每一个位置的字符都同样,那么相等,不然不相等。
(4)若是两个值都是true,或是false,那么相等
(5)若是两个值都引用同一个对象或是函数,那么相等,不然不相等
(6)若是两个值都是null,或是undefined,那么相等
Array.splice(begin, deleteCount, addItem1, addItem2...)
// a的初始值:[1,2,3,4,5]
var b = arr.splice(1,2)
// a: [1,4,5]
// b: [2,3]
var c = arr.splice(1,2,777,888)
// a: [1,777,888,4,5]
// b: [2,3]
复制代码
slice() 方法将数组中一部分元素浅复制存入新的数组对象,而且返回这个数组对象。
语法:arr.slice([start[, end]])
参数 start
指定复制开始位置的索引,end
若是有值则表示复制结束位置的索引(不包括此位置)。
若是 start
的值为负数,假如数组长度为 length
,则表示从 length+start 的位置开始复制,此时参数 end 若是有值,只能是比 start 大的负数,不然将返回空数组。
slice方法参数为空时,同concat方法同样,都是浅复制生成一个新数组。
var array = ["one", "two", "three","four", "five"];
console.log(array.slice()); // ["one", "two", "three","four", "five"]
console.log(array.slice(2,3)); // ["three"]
复制代码
浅复制 是指当对象的被复制时,只是复制了对象的引用,指向的依然是同一个对象。下面来讲明slice为何是浅复制。
var array = [{color:"yellow"}, 2, 3];
var array2 = array.slice(0,1);
console.log(array2); // [{color:"yellow"}]
array[0]["color"] = "blue";
console.log(array2); // [{color:"bule"}]
复制代码
因为slice是浅复制,复制到的对象只是一个引用,改变原数组array的值,array2也随之改变。
同时,稍微利用下 slice 方法第一个参数为负数时的特性,咱们能够很是方便的拿到数组的最后一项元素,以下:
console.log([1,2,3].slice(-1));//[3]
join() 方法将数组中的全部元素链接成一个字符串。
语法 arr.join('xxx')
var b = arr.join(','); // b: '1,2,3'
var b = arr.join('*'); // b: '1*2*3'
复制代码
数组中添加值。
concat() 方法将传入的数组或者元素与原数组合并,组成一个新的数组并返回。
indexOf() 方法用于查找元素在数组中第一次出现时的索引,若是没有,则返回-1。
语法:arr.indexOf(element, fromIndex=0)
element 为须要查找的元素。
fromIndex 为开始查找的位置,缺省默认为0。若是超出数组长度,则返回-1。若是为负值,假设数组长度为length,则从数组的第 length + fromIndex项开始往数组末尾查找,若是length + fromIndex<0 则整个数组都会被查找。
indexOf使用严格相等(即便用 === 去匹配数组中的元素)。
var array = ['abc', 'def', 'ghi','123'];
console.log(array.indexOf('def')); // 1
console.log(array.indexOf('def',-1)); // -1 此时表示从最后一个元素日后查找,所以查找失败返回-1
console.log(array.indexOf('def',-4)); // 1 因为4大于数组长度,此时将查找整个数组,所以返回1
console.log(array.indexOf(123)); // -1, 因为是严格匹配,所以并不会匹配到字符串'123'
复制代码
includes() 方法基于ECMAScript 2016(ES7)规范,它用来判断当前数组是否包含某个指定的值,若是是,则返回 true,不然返回 false。
语法:arr.includes(element, fromIndex=0)
element 为须要查找的元素。
fromIndex 表示从该索引位置开始查找 element,缺省为0,它是正向查找,即从索引处往数组末尾查找。
var array = [-0, 1, 2];
console.log(array.includes(+0)); // true
console.log(array.includes(1)); // true
console.log(array.includes(2,-4)); // true
复制代码
以上,includes彷佛忽略了 -0 与 +0 的区别,这不是问题,由于JavaScript一直以来都是不区分 -0 和 +0 的。
你可能会问,既然有了indexOf方法,为何又造一个includes方法,arr.indexOf(x)>-1不就等于arr.includes(x)?看起来是的,几乎全部的时候它们都等同,惟一的区别就是includes可以发现NaN,而indexOf不能。
var array = [NaN];
console.log(array.includes(NaN)); // true
console.log(arra.indexOf(NaN)>-1); // false
复制代码
(这里你们能够考虑下 indexOf 和 includes 的效率谁高,和底层实现)
数组函数有不少,上面只列举了经常使用的几个,发现一篇很全的数组函数文章,很是棒,感谢做者分享,推荐给你们:louiszhai.github.io/2017/04/28/…
string.split(separator,limit)
复制代码
substr() 方法返回字符串指定位置开始的指定数量的字符。
语法:str.substr(start[, length])
start 表示开始截取字符的位置,可取正值或负值。取正值时表示start位置的索引,取负值时表示 length+start位置的索引。
length 表示截取的字符长度。
var str = "Yesterday is history. Tomorrow is mystery. But today is a gift.";
console.log(str.substr(47)); // today is a gift.
console.log(str.substr(-16)); // today is a gift.
复制代码
数组函数有不少,上面只列举了经常使用的几个,发现一篇很全的字符串函数文章,很是棒,感谢做者分享,推荐给你们:louiszhai.github.io/2016/01/12/…
该方法仅在目标属性为对象自身属性时返回true,而当该属性是从原型链中继承而来或根本不存在时,返回false。
var o = {prop:1};
o.hasOwnProperty('prop'); // true
o.hasOwnProperty('toString'); // false
o.hasOwnProperty('formString'); // false
复制代码
该方法主要用于建立一个新对象,并为其设置原型,用(上述)属性描述符来定义对象的原型属性。
var parent = {hi: 'Hello'};
var o = Object.create(parent, {
prop: {
value: 1
}
});
o.hi; // 'Hello'
// 得到它的原型
Object.getPrototypeOf(parent) === Object.prototype; // true 说明parent的原型是Object.prototype
Object.getPrototypeOf(o); // {hi: "Hello"} // 说明o的原型是{hi: "Hello"}
o.hasOwnProperty('hi'); // false 说明hi是原型上的
o.hasOwnProperty('prop'); // true 说明prop是原型上的自身上的属性。
复制代码
如今,咱们甚至能够用它来建立一个彻底空白的对象,这样的事情在ES3中但是作不到的。
var o = Object.create(null);
typeof o.toString(); // 'undefined'
复制代码
数组函数有不少,上面只列举了经常使用的几个,发现一篇很全的对象函数文章,很是棒,感谢做者分享,推荐给你们:www.lxchuan12.cn/js-object-a…
什么是相似数组对象,举个例子:
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
复制代码
类数组转换的几种方式
ES6 扩展运算符进行转换
var arr1 = [...arrayLike]; // ['a','b','c']
复制代码
ES6 中的 Array.from
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
复制代码
Array.from()
的另外一个应用是,将字符串转为数组,而后返回字符串的长度。
function countSymbols(string) {
return Array.from(string).length;
}
复制代码
ES5中 Array.prototype.slice.call()
function test(a,b,c,d) {
var arg = Array.prototype.slice.call(arguments,1);
alert(arg);
}
test("a","b","c","d"); //b,c,d
复制代码
Array.prototype.slice.call(arguments)
能将具备length
属性的对象转成数组,除了IE
下的节点集合(由于ie下的dom对象是以com对象的形式实现的,js对象与com对象不能进行转换)
高阶函数相关内容能够看这篇文章。 高阶函数详解与实战 在实现无限叠加,数组拍平,去重等均可以用到高阶函数。
让你手写代码的时候,能够考虑一下使用 ES6 方式如何简洁实现。(这里你们能够去看一下ES6的扩展运算符...,set集合,箭头函数等经常使用的ES6知识点,附上ES6阮一峰老师书籍学习地址:es6.ruanyifeng.com/)
Node.js 中有一个queryString模块,能够实现将 urlStr 主机地址后面的参数转化为对象。
let urlStr = 'http://www.inode.club?name=koala&study=js&study=node';
复制代码
转换结果以下:
{ name: 'koala', study: [ 'js', 'node' ] }
复制代码
你能够现本身实现一下,看完本篇想一下流程
let urlStr = 'http://www.inode.club?name=koala&study=js&study=node'
// 参数转成对象
function queryString(request){
let params = request.split('?')[1];
let param = params.split('&');
let obj = {};
for (let i = 0;i<param.length;i++){
let paramsA = param[i].split('=');
let key = paramsA[0];
let value = paramsA[1];
if(obj[key]){
obj[key] = Array.isArray(obj[key])?obj[key]:[obj[key]];
obj[key].push(value);
}else{
obj[key] = value;
}
}
return obj;
}
console.log(queryString(urlStr));
复制代码
本文是一个应对手写代码的大纲,一下函数并无所有列全,按照大纲复习一下,再去看一些常考的手写代码问题,我的感受能好记并且清晰了一些,但愿对你们有所帮助。