前段时间刚好复习了正则,所以把正则的写前面
题目描述:实现将一个数从各位数开始,每距离一个千分位添加一个分割符 ‘ ,’
如输入: 12345678.32423432
输出:12,345,678.32423432
这道题在最近一两个月的面试中遇到两次,可以分别有字符串和数组的一些方法结合解决,也可以用正则解决,我觉得正则可能更难以理解
第一种方法:
const num = 12345678.32423432
function getForm(num) {
let arr = num.toString().split('.')
// 取出整数部分
let arr0 = arr[0].split('')
// console.log(arr0);
let stack = []
let i = 0
while (arr0.length > 0) {
// 没3位添加一个分割符
if (i % 3 == 0 && i !== 0) {
stack.push(',')
}
stack.push(arr0.pop())
i++
}
let res = stack.reverse()
console.log(res);
// 考虑是否存在小数部分
if (arr[1]) {
return res.join('') + '.' + arr[1]
} else {
return res.join('')
}
}
console.log(getForm(num));
第二种方法用正则解决:
function regForm(num) {
// console.log(num.toString());
return num.toString().replace(/\d+/, (p) => {
return p.replace(/\d(?=(\d{3})+$)+/g, (p1, p2) => {
return p1 + ','
})
})
}
实现string原型上的trim()方法:
解决方法有很多,我常用的是下面这两个
String.prototype.my_trim = function () {
return this.replace(/^\s+/, '').replace(/\s+$/, ' ')
}
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
}
取出 :'sadddddddddddddsssssssssssssdddddddasddd’重复的字符
最终为 [“ddddddddddddd”, “sssssssssssss”, “ddddddd”, “ddd”]
面试腾讯暑期实习生遇到此题,我当时真的菜没做出来,回过头来发现真简单
let a = 'sadddddddddddddsssssssssssssdddddddasddd'
console.log(a.match(/([a-z])\1+/ig));
const url = 'http://www.baidu.com/we/index.html?id=098&aaa=123&ccc=456'
function parseParam(url) {
let arr = url.split('?')[1].split('&')
console.log(arr);
let obj = {
}
arr.forEach(item => {
console.log(item.split('='));
let [key, value] = item.split('=')
if (/^\d+$/.test(value)) {
value = parseInt(value)
}
obj[key] = value
})
return obj
}
console.log(parseParam(url));
自我感觉这部分的手写题能够全部理解的话,对于闭包的理解会更加深刻
高阶函数应该就是输入参数是函数,返回也是一个函数
var add = function (a) {
return a + 1
}
function memo(fn) {
const cache = {
}
return function (...ret) {
let key = JSON.stringify(ret)
return cache[key] || (cache[key] = fn.apply(fn, [...ret]))
}
}
const adder = memo(add)
console.log(adder);
console.log(adder(1));
console.log(adder(1));
console.log(adder(3));
console.log(adder(3));
这几个函数只有把手写学会才会理解的更深刻
点击查看详细解说分别手写call,apply,bind
具体要求就是能使本来函数add(1,2,3)变成add(1)(2)(3)
具体思路就是将参数用递归的方式一个一个的传入目标函数
function curry(fn, args) {
var ofArgs = args || []
var len = fn.length
var self = this
//
return function (...ret) {
// ret是后面传入的函数
var currentArg = [...ofArgs, ...ret]
console.log(currentArg);
// 如果当前参数数组的长度小于fn的要求长度 那么继续递归
if (len > currentArg.length) {
return curry.call(self, fn, currentArg)
}
return fn.apply(self, currentArg)
}
}
const add = function (a, b, c) {
return a + b + c
}
const newFn = curry(add)
// newFn(1)(2)(3)
console.log(newFn(1)(2)(3));
防抖和节流
拷贝一个地址不同其他完全相同的对象
function deepClone(obj) {
if (typeof obj !== 'object') {
return obj
}
let res
// 数组和对象的限定
if (Array.isArray(obj)) {
res = []
} else {
res = {
}
}
//一层层递归赋值
for (let key in obj) {
console.log(key);
if (obj.hasOwnProperty(key)) {
res[key] = obj[key]
}
}
return res
}
let book = {
name: "局外人",
types: {
t1: "中文版",
t2: "英文版",
a: {
c: 2
}
}
}
5
let book_clone = deepClone(book)
console.log(book_clone === book);
console.log(book_clone);
深度比较两个对象的值是否完全相同
function isEqual(obj1, obj2) {
// 判断两个是不是对象
if (typeof obj1 !== 'object' && typeof obj2 !== 'object'
&& obj1 !== null && obj2 !== null) {
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
// 先取出obj1和obj2的keys,比较个数
let leng1 = Object.keys(obj1).length
let leng2 = Object.keys(obj2).length
// console.log(leng1);
if (leng1 !== leng2) {
return false
}
// 以obj1为基准和obj2递归比较
for (let key in obj1) {
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
return true
}
const obj1 = {
a: 2,
b: 'a',
c: {
e: 'f',
f: 's'
}
}
const obj2 = {
a: 2,
b: 'a',
c: {
e: 'f',
f: 's',
// a: 41
}
}
console.log(isEqual(obj1, obj2));
最简单的去重:用set
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {
}, {
}];
console.log(unique(arr));
// 注意Set是能够去掉NaN的重复的
// [...new Set(arr)]
function unique(arr) {
return Array.from(new Set(arr))
}
使用indexOf无法去除 NaN的重复 indexOf(NaN)永远等于-1
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {
}, {
}];
console.log(unique(arr));
function unique(arr) {
if (!Array.isArray(arr)) {
{
console.log('is nor a array');
}
}
const res = []
arr.forEach(element => {
if (res.indexOf(element) === -1) {
res.push(element)
}
});
return res
}
includes也是能够去掉NaN的重复的
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {
}, {
}];
console.log(unique(arr));
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('is not array')
}
const new_arr = []
arr.forEach(e => {
if (!new_arr.includes(e)) {
new_arr.push(e)
}
})
return new_arr
}
splice使用此函数特别需要注意的一点:会改变数组,改变数组的长度
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {
}, {
}];
console.log(unique(arr));
function unique(arr) {
let len = arr.length
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] == arr[j]) {
// console.log(arr);
arr.splice(j, 1)
// console.log(arr);
j--;
}
}
}
return arr
}
用filter+hasOwnProperty两个空对象也能去重
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {
}, {
}];
console.log(unique(arr));
function unique(arr) {
var obj = {
};
return arr.filter(function (item, index, arr) {
console.log(item);
console.log(typeof item + item);
// console.log(obj[typeof item + item]);
// 判断obj对象是否有这个属性,如果有(说明数组元素重复)直接返回false过滤掉数组中的重复元素
// 如果没有,为obj对象添加上这个属性
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
主要用到的api是Match.random和set
function disorder(arr) {
let len = arr.length
let res = []
let remember = new Set()
// console.log(remember.size);
while (remember.size < len) {
let randomNum = Math.floor(len * Math.random())
if (!remember.has(randomNum)) {
res.push(arr[randomNum])
remember.add(randomNum)
}
}
return res
}
console.log(disorder([1, 2, 3, 4, 5, 6, 7, 8, 9]));
先具体了解一下filter -MDN
下面的call后面的参数和上面的参数是对应的
Array.prototype.my_filter = function (fn, context) {
if (Object.prototype.toString.call(this) !== '[object Array]') {
throw new TypeError('is not a array')
}
let newArr = []
for (let i = 0; i < this.length; i++) {
let t = fn.call(context, this[i], i, this)
if (t) {
newArr.push(this[i])
}
}
return newArr
}
const arr = [1, 2].my_filter(x => x == 2)
console.log(arr);
要实现拍平一个数组,可以用concat或剩余运算符
第一种实现用reduce+递归
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", {
name: "弹铁蛋同学" }];
function flat(arr) {
return arr.reduce((pres, cur) => {
return pres.concat(Array.isArray(cur) ? flat(cur) : cur)
}, [])
}
第二种实现方式用栈
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", {
name: "弹铁蛋同学" }];
console.log(flat(arr, 2));
function flat(arr) {
let stack = [].concat(arr)
let res = []
while (stack.length !== 0) {
const i = stack.pop()
if (Array.isArray(i)) {
stack.push(...i)
} else {
res.unshift(i)
}
}
return res
}
一个new的实现需要经过这几步
function New() {
// 创建一个新对象
const obj = {
}
// 获取第一个参数即为构造函数
const constr = [...arguments].shift()
console.log(constr);
console.log(arguments);
// 将obj的原型指向构造函数的原型对象,这样obj就可以访问构造函数原型上的属性
obj.__proto__ = constr.prototype
// 执行构造函数
constr.apply(obj, [...arguments].slice(1))
return obj
}
function Parent(age, name) {
this.age = age
this.name = name
this.sayAge = function () {
console.log('this.age :', this.age)
}
}
Parent.prototype.sayName = function () {
console.log('this.name :', this.name)
}
let son = new Parent(18, 'lining')
let newSon = New(Parent, 18, 'lining')
console.log('son :', son) // son : Parent { age: 18, name: 'lining', sayAge: [Function] }
console.log('newSon :', newSon)
instanceof主要实现原理就是只要右边变量的prototype在左边变量的原型链上即可
因此instanceof 在查找的过程中会 遍历左边变量的原型链,知道找到右边变量的prototype
function My_instanceof(left,right){
let rightProto = right.prototype;
left = left.__proto__
while(true){
if(left == null){
return false
}
if(left === rightProto){
return true
}
left = left.__proto__
}
}
console.log( My_instanceof(1,Number));