高阶JS面试题NINE

生成括号 n = 1 ()

思路:f(n) 的值永远都是 f(n-1) 的结果 里面每一项push ()

export function generateParenthesis(initn: number): string[] {
    const func = (n: number): string[] => {
        if (n == 1) {
            return ['()']
        }

        let res = func(n - 1)
        
        let pjRes = []
        for (let i = 0; i < res.length; i++) {
            let str = res[i]
            let sL = str.length
            for (let j = 0; j < sL; j++) {
                pjRes.push(`${str.slice(0, j)}()${str.slice(j, sL)}`)
            }
        }
        return pjRes
    }

    return Array.from(new Set(func(initn)))
}

console.log(generateParenthesis(3), 'na');

实现深拷贝包括 map set 类型

  1. map
  2. set
  3. 函数
  4. 循环引用
  5. 深层数据拷贝
export function cloneDeep(obj: any, map = new WeakMap()): any {
    if (typeof obj !== 'object' || obj == null) return obj

    // 避免循环引用
    const objFromMap = map.get(obj)
    if (objFromMap) return objFromMap

    let target: any = {}
    map.set(obj, target)

    // Map
    if (obj instanceof Map) {
        target = new Map()
        obj.forEach((v, k) => {
            const v1 = cloneDeep(v, map)
            const k1 = cloneDeep(k, map)
            target.set(k1, v1)
        })
    }

    // Set
    if (obj instanceof Set) {
        target = new Set()
        obj.forEach(v => {
            const v1 = cloneDeep(v, map)
            target.add(v1)
        })
    }

    // Array
    if (obj instanceof Array) {
        target = obj.map(item => cloneDeep(item, map))
    }

    // Object
    for (const key in obj) {
        const val = obj[key]
        const val1 = cloneDeep(val, map)
        target[key] = val1
    }

    return target
}

const a: any = {
    set: new Set([1, 2, 3, 4]),
    map: new Map([['x', 10], ['y', 20]]),
    info: {
        city: '北京'
    },
    fn: () => { },
}

a.self = a
console.log(cloneDeep(a), 'cloneDeep');

[‘1’, ‘2’, ‘3’].map(parseInt)

上面代码相当于

['1', '2', '3'].map((item, index) => {
    parseInt(item, index)
    // parseInt 
    // 转换的数字
    // raIndex 第二个参数是 进制 单位 (2-32)
    /**
     * parInt('1', 0) // 0 的时候 说明没传 是2进制
     * parInt('2', 1) // 1 的时候 没有 1 进制 所以返回 NAN
     * parInt('3', 2) // 2 的时候 3 不属于二进制的内容(011101010101010这种只有 0 和 1 才属于二进制) 
     */
})

所以最终返回 [1, NaN, NaN]

函数的参数传递就是赋值传递

const fn = (x, y) => {
    // 这里就相当于 x = num; y = obj
}
let num = 'aaa'
let obj = { name: 'glack' }
fn(num, obj)

数组转为树节点(针对有序的二维数组排列树节点)

const arr1 = [
    { id: 1, name: '部门A', parent_id: 0 },
    { id: 2, name: '部门B', parent_id: 1 },
    { id: 3, name: '部门C', parent_id: 1 },
    { id: 4, name: '部门D', parent_id: 2 },
    { id: 5, name: '部门E', parent_id: 4 },
    { id: 6, name: '部门F', parent_id: 4 },
];
/**
 * 针对有序的二维数组排列树节点
 * @param arr arr
 */
interface ITreeNode {
    id: number,
    name: string,
    children?: ITreeNode[]
}
interface ITreeItem {
    id: number,
    name: string,
    parent_id: number
}
export const treeTransform = (arr: ITreeItem[]): ITreeNode | null => {
    // 用于id 和 treeNode 的映射
    let treeMap: Map<number, ITreeNode> = new Map()

    let root: ITreeNode | null = null

    // 定义tree node 并加入 map
    arr.forEach(({ id, name }) => {
        const treeNode: ITreeNode = { id, name }
        treeMap.set(id, treeNode)
    })

    arr.forEach(item => {
        const { id, parent_id } = item

        const treeNode = treeMap.get(id)

        // 找到 parentNode 并加入到它的 children
        const parentNode = treeMap.get(parent_id)
        if (parentNode && treeNode) {
            if (parentNode.children == null) parentNode.children = []
            parentNode.children.push(treeNode)
        }

        // console.log(parentNode, 'parentNode');
        // console.log(treeNode, 'treeNode');

        // 找到根节点
        if (parent_id === 0) {
            // @ts-ignore
            root = treeNode
        }
    })

    return root
}
let result = treeTransform(arr1)
console.log(result);
怎么证明 root 的子元素一直在变?
  • 在 map 外部修改map里面的children 元素 里面的元素会跟着修改
let map = new Map();
map.set('arr', []);
let arr = map.get('arr');
arr.push('a');
map.get('arr') // ['a']

树转为数组

let res: any[] = []
let treeTransformArray = (root: any[], parent_id: number = 0) => {
    let arr = Array.isArray(root) ? root : [root]

    // 遍历所有元素
    arr.forEach(item => {
        if (item.children) {
            treeTransformArray(item.children, item.id)
        }

        res.push({ id: item.id, name: item.name, parent_id })
    })
}
// @ts-ignore 这里的result 是上面的结果
treeTransformArray(result)
console.log(res, 'zz');

原型链面试题

function Foo(){
    Foo.a = function name() {
        console.log(1);
    }
    this.a = function () {
        console.log(2);
    }
}

Foo.prototype.a = function(){ console.log(3); }

Foo.a = function(){ console.log(4) }

Foo.a()
let obj = new Foo()
obj.a()
Foo.a()

// 4 2 1

promise

Promise.resolve().then(() => {
    console.log(1)
}).then(() => {
    console.log(2)
}).then(() => {
    console.log(3)
}).then(() => {
    console.log(4)
}).then(() => {
    console.log(5)
}).then(() => {
    console.log(6)
})

Promise.resolve().then(() => {
    console.log(10)
}).then(() => {
    console.log(20)
}).then(() => {
    console.log(30)
}).then(() => {
    console.log(40)
}).then(() => {
    console.log(50)
}).then(() => {
    console.log(60)
})
// 1 10 2 20 3 30 4 40 5 50 6 60

多个promise 已完成的状态同时执行 .then 的链式调用
所以 .then会交替执行

Promise.resolve().then(() => {
    console.log(1)
    return Promise.resolve(100)
}).then((res) => {
    console.log(res)
}).then(() => {
    console.log(3)
}).then(() => {
    console.log(4)
}).then(() => {
    console.log(5)
}).then(() => {
    console.log(6)
})

Promise.resolve().then(() => {
    console.log(10)
}).then(() => {
    console.log(20)
}).then(() => {
    console.log(30)
}).then(() => {
    console.log(40)
}).then(() => {
    console.log(50)
}).then(() => {
    console.log(60)
})
// 1 10 20 30 100 40 3 50 4 60 5 6

.then 中返回promise 实例会慢两拍(三步) 因为需要时间把自己 penging 变成 fulfilled状态

setState 同步异步问题

didMount(){
    // 假设默认值是 0 
    this.setState({
        val: this.state.val + 1
    })
    console.log(this.state.val) // 异步更新 0 
    this.setState({
        val: this.state.val + 1
    })
    console.log(this.state.val) // 异步更新 0 

    setTimeout(() => {
        this.setState({
            val: this.state.val + 1
        })
        console.log(this.state.val) // 同步更新 2
        this.setState({
            val: this.state.val + 1
        })
        console.log(this.state.val) // 同步更新 3
    })
}

setState 会合并

如果同时多个setState 都要修改val 那么会优先使用 下面的哪个setState语句

不合并的情况

传入函数: this.setState((prev, props) => { return …})

同步更新

setState 同步更新:在setTimeout setInterval promise.then
自定义的 dom 事件
ajax 回调
例子:

document.getElementById('id').addEventlistener('click', () => {
    this.setState({
        val: this.state.val + 1
    })
    console.log(this.state.val) // 同步更新
})

setState是同步 不过只不过是当做异步处理

React 18 中不一样了

可以在setTimeout 中 异步更新了
Auto Batch
需要将 ReactDOM.render 替换为 ReactDOM.createRoot

a.x 比赋值的优先级高

let a = { n: 1 }
let b = a
a.x = a = { n: 2 }

console.log(a.x) // undefined
console.log(b.x) //  n: 2

字符串对象的key 只能是字符串或者 symbol 类型

let a = {}, b = '123', c = 123
a[b] = 'b'
a[c] = 'c'

// log => a[b] c


let a = {}, b = Symbol('123'), c = Symbol('123')
a[b] = 'b'
a[c] = 'c'

// log => a[b] b

设计一个前端统计 SDK


  • 统计的范围:性能 错误 pv 自定义事件
  • 发送数据使用 img 的 src 来发送get请求
  • 报错统计结合 Vue React 报错

sourcemap 的作用

  • 线上代码压缩报错,sourcemap 可解决这个问题
  • 可以给压缩后的文件识别报错的行数,找到错误的地放

如何设置?

  • webpack 通过 devtool 配置sourcemap
    • eval - js 在 eval(…) 中 不生产sourcemap
    • source-map 生成单独的 map 文件, 并在js 最后指定
    • eval-source-map,js在eval(…) 中,sourcemap内嵌
    • inline-source-map - sourcemap 内嵌到 js 中
    • cheap-source-map - sourcemap 中只有行信息,没有列
    • eval-cheap-source-map 同上,只有行没有列
      非开源项目:不要泄漏sourcemap ! 否则容易被反解

SPA 和 MPA 怎么选择

  • SPA 特点

    • 功能较多,一个页面展示不完
    • 以操作为主,非展示为主
    • 适合一个综合Web应用
  • MPA

    • 功能较少,展示为主,例如:分享页,新闻详情页,微信公众号发出的页面

你可能感兴趣的:(javascript,javascript,前端,开发语言)