Skip to main content

6 posts tagged with "手撕程序题"

View All Tags

URL 解析为对象

将以下输入的 URL 转化为对象:

http://www.baidu.com/s?wd=春节&name=justin
{
wd: '春节',
name: 'justin'
}

这道题重点在于分割,将 ? 后的键值对取出来,并放置于对象中,不同的键值对通过 & 符号分割。具体代码如下(这里也只给出一种解法):

let urlToJson = (url = window.location.href) => {
// 箭头函数默认传值为当前页面url
url = url.encodeURIComponent()
let obj = {},
index = url.indexOf('?'),
params = url.substr(index + 1)
if (index != -1) {
let parr = params.split('&')
for (let i of parr) {
let arr = i.split('=')
obj[arr[0]] = arr[1]
}
}
return obj
}

flat、拍平数组、自己实现拍平数组的效果

这里只提供两个比较简单的实现,后面还会涉及到使用reduce和栈、使用Generator、原型链等等,详细见:面试官连环追问:数组拍平(扁平化) flat 方法实现 - 知乎 (zhihu.com)

  1. 最简单的遍历实现
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, 'string', { name: '弹铁蛋同学' }]
// concat + 递归
function flat(arr) {
let arrResult = []
arr.forEach((item) => {
if (Array.isArray(item)) {
arrResult = arrResult.concat(flat(item)) // 递归
// 或者用扩展运算符
// arrResult.push(...arguments.callee(item));
} else {
arrResult.push(item)
}
})
return arrResult
}
flat(arr)
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];
  1. 传入数组控制递归层数
// reduce + 递归
function flat(arr, num = 1) {
return num > 0
? arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? flat(cur, num - 1) : cur), [])
: arr.slice()
}
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, 'string', { name: '弹铁蛋同学' }]
flat(arr, Infinity)
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

手写 reduce

注意:不能使用箭头函数,箭头函数中没有 this 所有会导致 sourcearr 为空对象

Array.prototype.fakereduce = function (fn, initnumber = 0) {
let sum_increase = initnumber
let sourcearr = this
for (let i = 0; i < sourcearr.length; i++) {
sum_increase = fn(sum_increase, sourcearr[i])
}
return sum_increase
}

这个只是最基本的,只包含前两个参数,也没有检测是否为函数。(下面加入判断情况)

// 判断调用对象是否为数组
if (Object.prototype.toString.call([]) !== '[object Array]') {
throw new TypeError('not a array')
}
// 判断调用数组是否为空数组
const sourceArray = this
if (sourceArray.length === 0) {
throw new TypeError('empty array')
}
// 判断传入的第一个参数是否为函数
if (typeof fn !== 'function') {
throw new TypeError(`${fn} is not a function`)
}

自己实现 promise all

Promise.newAll = function (promiseArr) {
let results = []
return new Promise((reslove, reject) => {
promiseArr.forEach((item_promise) => {
item_promise.then((res) => results.push(res)).catch((err) => reject(err))
})
return reslove(results)
})
}

构造函数与实例对象间的属性问题

以下代码输出什么?

function Otaku() {
this.b = 1
this.c = 2
}
var person = new Otaku()
Otaku.prototype.b = 4
person.c = 5
console.log('1:', person.b)
console.log('2:', person.c)
person.__proto__.b = 10
console.log('3:', person.b)
console.log('4:', Otaku.prototype.b)

这道题目涉及到构造函数与实例对象之间的知识点,背后同样涉及原型与原型链的问题,详细见JavaScript 深入之从原型到原型链

我们先来揭晓答案:

1: 1
2: 5
3: 1
4: 10

你有全答对吗?下面我们一起来看看这道题。这道题首先给了一个构造函数 Otaku,这个构造函数有两个属性 bc,然后使用 new 创建了它的实例对象 person,如果你了解原型,那么你肯定知道实例对象 person 已经获得了构造函数中的属性。

function Otaku() {
this.b = 1
this.c = 2
}
var person = new Otaku() // person.b = 1; person.c = 2

看到这里你会发现构造函数 Otaku 有一个属性 prototype,这个构造函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,也就是这个例子中的 person 原型。也就是说 Otaku.prototype.b = 4; 这条语句中的 b 实际指向的是原型的 b。

function Otaku() {
this.b = 1
this.c = 2
}
var person = new Otaku()
Otaku.prototype.b = 4 // 修改的是 person 的原型的属性 b
person.c = 5 // 修改的是 person 的 c
console.log('1:', person.b) // person.b = 1
console.log('2:', person.c) // person.c = 5

看到这里,我们又发现 person 的属性 __proto__, 这个属性也指向 person 的原型,所以这句话的 b 属性也是指向原型的 b

function Otaku() {
this.b = 1
this.c = 2
}
var person = new Otaku() // person.b = 1; person.c = 2
Otaku.prototype.b = 4 // 修改的是 person 的原型的属性 b
person.c = 5 // 修改的是 person 的 c
console.log('1:', person.b) // person.b = 1
console.log('2:', person.c) // person.c = 5
person.__proto__.b = 10 // 修改的事 preson 的原型的属性b
console.log('3:', person.b) // person.b = 1
console.log('4:', Otaku.prototype.b) // Otaku.prototype.b = 10

这就是结果了,关于这道题涉及的知识点,重点还是在原型以及原型链上。