前端知识点——常见手撕题

这篇博客详细介绍了JavaScript的高级技术实现,包括URL解析、防抖与节流、函数柯里化、setTimeout实现setInterval、字符串处理、数组操作、对象拷贝、bind、apply、call方法、原生API实现、图片懒加载、简单路由实现、发布订阅模式、HTTP请求(jsonp和AJAX)、Promise系列、链表、数据双向绑定、对象数组去重、函数判断类型以及JS继承等。通过示例代码展示了各种功能的实现细节,帮助读者深入理解JavaScript的高级概念和技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、解析URL

二、功能函数实现

1、防抖和节流

(1)防抖

(2)节流

2、函数柯里化

(1) 

(2)

3、setTimeout实现setInterval

三、字符串

1、清除字符串前后空格

(1) .replace()

(2) 循环

2、驼峰命名

四、数组

1、 数组拍平

(1)、递归法

(2)、toString & split

(3)、join & split

2、数组去重

3、根据属性,对象数组去重

4、数组乱序

五、浅拷贝、深拷贝

1、浅拷贝

2、深拷贝 

六、bind,apply,call

1、bind函数

2、apply函数

 3、call函数

 七、实现原生API

1、实现new

2、实现create()

3、instanceof实现

4、实现reduce()

5、实现forEach

6、用reduce实现map

7、实现indexOf

8、实现Object.assign()

八、图片懒加载

九、实现一个简单路由

1、hash路由

2、history路由

十、 发布订阅模式(event bus)

十一、HTTP请求

1、jsonp

2、实现AJAX

(1)简易版

(2)promise实现版

十二、Promise系列

1、promise

2、promise.all

3、promise.race

4、promise经典题

十三、链表

1、单链表

2、双向链表

十四、数据双向绑定

(1)vue2.0

(2)proxy双向数据绑定

十五、判断两个对象是否相等

十六、判断一个对象是空对象

十七、实现函数判断类型的函数

十八、数据绑定基本实现

十九、有一个祖先树状 json 对象,当一个人有一个儿子的时候,其 child 为其儿子对象,如果有多个儿子,child 为儿子对象的数组。

二十、js继承




一、解析URL

 <script>
        var url = "https: //zhidao.baidu.com/question/1768422895052400180.html?fr=iks&word=slice&ie=gbk"

        function getUrl(url) {
            let s = url.split('?')[1]
            let str = s.split('&')
            let obj = {}
            for (let i = 0; i < str.length; i++) {
                let key = str[i].split('=')[0]
                let value = str[i].split('=')[1]
                obj[key] = value
            }
            return obj
        }

        console.log(getUrl(url));
    </script>

二、功能函数实现

1、防抖和节流

(1)防抖

频繁操作时,只执行最后一次

(2)节流

频繁操作时,只有在一定时间内执行一次

<button>btn</button>
    <script>
        function test(fn, delay) {
            let timer = null
            return function() {
                if(timer){
                   return
                }
                timer = setTimeout(() => {
                    fn()
                    timer = null
                }, delay)
            }
        }

        let btn = document.querySelector('button')
        btn.addEventListener('click', test(() => console.log('2233'), 1000))
    </script>

2、函数柯里化

(1) 

(2)

(3)

<script>
        function add() {
            var arg = [...arguments]

            let fn = function() {
                arg.push(...arguments)
                return fn
            }
            fn.toString = function() {
                return arg.reduce((sum, cur) => sum + cur)
            }
            return fn
        }

        console.log(add(1)(2, 3).toString());
    </script>

3、setTimeout实现setInterval

<script>
        function myInterval(fn, delay) {
            setTimeout(() => {
                fn.call(this)
                myInterval(fn, delay)
            }, delay)
        }

        myInterval(() => {
            console.log(123);
        }, 1000)
    </script>

三、字符串

1、清除字符串前后空格

(1) .replace()

(2) 循环

//(2)
function myTrim(str) {//记录前后空格的个数,最后对字符串进行截取
	let first=0,last=str.length
	for (let i in str) {
		if (str[i]===' ') {
			first++
		} else {
			break
		}
	}
	for (let i=last;i>first;i--) {
		if (str[i]===' ') {
			last--
		} else {
			break
		}
	}
	return str.substr(first,last-first)
}

2、驼峰命名

var s1 = "get-element-by-id"

// 转化为 getElementById

var f = function(s) {
    return s.replace(/-\w/g, function(x) {
        return x.slice(1).toUpperCase();
    })
}

四、数组

1、 数组拍平

(1)、递归法

(2)、toString & split

<script>
    function flatten(res) {
            // split 会将 string 中按照括号里的内容进行分割并存入一个新数组,返回数组
            // map 会对数组中的所有数据按照函数进行处理后返回一个处理后的数组
            return res.toString().split(',').map((item) => {
                return Number(item)
            })
        }

        let res = [1, 2, 3, [4, 5, [6]]]
        console.log(flatten(res));
</script>

(3)、join & split


        function flatten(res) {
            // join 把数组中的所有元素转换为一个字符串
            // split 会将 string 中按照括号里的内容进行分割并存入一个新数组,返回数组
            // map 会对数组中的所有数据按照函数进行处理后返回一个处理后的数组
            return res.join().split(',').map((item) => {
                return parseInt(item)
            })
        }

2、数组去重

<script>
        let arr = [1, 2, 3, 3, 4, 4, 5, 6, 6]
            //(1) set
            /* arr = new Set(arr)
            console.log(arr); */

        //(2) indexOf
        let res = []
        for (let num of arr) {
            if (res.indexOf(num) == -1) {
                res.push(num)
            }
        }
        console.log(res)
    </script>

3、根据属性,对象数组去重

const responseList = [
  { id: 1, a: 1 },
  { id: 2, a: 2 },
  { id: 3, a: 3 },
  { id: 1, a: 4 },
];
const result = responseList.reduce((acc, cur) => {
    const ids = acc.map(item => item.id);
    return ids.includes(cur.id) ? acc : [...acc, cur];
}, []);
console.log(result); // -> [ { id: 1, a: 1}, {id: 2, a: 2}, {id: 3, a: 3} ]

4、数组乱序

// 著名的Fisher–Yates shuffle 洗牌算法
function shuffle(arr){
    let m = arr.length;
    while(m > 1){
        let index = parseInt(Math.random() * m--);
        [arr[index],arr[m]] = [arr[m],arr[index]];  //解构赋值
    }
    return arr;
}

五、浅拷贝、深拷贝

1、浅拷贝

// 1. ...实现
let copy1 = {...{x:1}}

// 2. Object.assign实现
let copy2 = Object.assign({}, {x:1})

2、深拷贝 

<script>
       // (1)
        function deepClone(obj) {
            if (obj == null || typeof obj != 'object') {
                return obj
            }
            // let result = obj instanceof Array ? [] : {}
            let result
            if (obj instanceof Array) {
                result = []
            } else {
                result = {}
            }
            for (let key in obj) { //in遍历键值,of遍历键名
                if (obj.hasOwnProperty(key)) {
                    /*  for in 可以遍历到myObject的原型方法method,如果不想遍历原型方法和属性的话,
                    所以for in更适合遍历对象,不要使用for in遍历数组
                    for of 遍历的只是数组内的元素,而不包括数组的原型属性method和索引name
                    for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象
                     可以在循环内部判断一下,hasOwnPropery方法可以判断某属性是否是该对象的实例属性 */
                    result[key] = deepClone(obj[key])
                }
            }
            return result
        }

        const obj = {
            name: 'zhangsan',
            address: {
                city: 'beijing'
            },
            arr: ['a', 'b', 'c']
        }
        let obj1 = deepClone(obj)
        obj1.address.city = '上海'
        console.log(obj1)
        console.log(obj);

       //(2)
        function deepClone2(obj) {
        var _obj = JSON.stringify(obj),//将JavaScript值转换为JSON字符串
        var objClone = JSON.parse(_obj);//将JSON字符串转为一个对象。
        return objClone;
        }
    </script>

六、bind,apply,call

1、bind函数

2、apply函数

 3、call函数

 七、实现原生API

1、实现new

<script>
        function myNew(constructor_fn, ...arg) {
            let obj = new Object()
            obj.__proto__ = constructor_fn.prototype
            let result = constructor_fn.apply(obj, arg)
            if (result instanceof Object) {
                return result
            } else {
                return obj
            }
        }

        function Person(uname, age) {
            this.uname = uname;
            this.age = age;
        }

        // let obj1 = new Person('vivian', 18);
        let obj2 = myNew(Person, 'vivian', 18);
        console.log(obj2);

2、实现create()

    <script>
        /* Object.create()方法用于创建一个新对象并将这个新对象的[[Prototype]]设置为特定的对象。另外,Object.create()方法还有一个可选的参数propertiesObject,
这个参数类似于Object.defineProperties()方法的第二个参数,指定了将要添加到新对象中的属性和属性的各个特性。 */
        function myCreate(object, properties) {
            // 创建构造函数,并将构造函数的原型对象设置为proto
            function Fn() {}
            Fn.prototype = object

            //创建新对象
            let obj = new Fn()
            if (properties && typeof properties === 'object') {
                Object.defineProperties(obj, properties)
            }

            return obj
        }

        //test
        let person = myCreate({
            name: "Alex"
        }, {
            age: {
                writable: false,
                enumerable: true,
                value: 18

            }
        });
        console.log(person.name, person.age);
    </script>

3、instanceof实现

<script>
        function myInstanceof(left, right) {
            let left_proto = left.__proto__  //两个_
            let right_proto = right.prototype
            while (true) {
                if (!left_proto) return false
                if (left_proto == right_proto) return true
                left_proto = left_proto.__proto__
            }
        }
        console.log(myInstanceof([], Array));
    </script>

4、实现reduce()

    <script>
        Array.prototype.myReduce = function(fn, init) {
            let [val, index] = init ? [init, 0] : [this[0], 1] //this表示调用myReduce方法的数组
            for (let i = index; i < this.length; i++) {
                val = fn(val, this[i], i, this)
            }
            return val
        }

        let res = [1, 2, 3, 4, 5]
        console.log(res.myReduce((val, next) => {
            return val + next
        }, 10));  //25
    </script>

5、实现forEach

<script>
        Array.prototype.myForEach = function(fn) {
            for (let i = 0; i < this.length; i++) {
                fn.call(this, this[i], i, this)
            }
        }

        let res = [1, 2, 3, 4, 5]
        res.myForEach((item, index, arr) => {
            console.log(item);
        })
    </script>

6、用reduce实现map

<script>
        //map 会返回一个新数组
        Array.prototype.myMap = function(fn, value) {
            if (typeof fn !== 'function') {
                throw new TypeError("arguments[0] is not a function");
            }
            let arr = this
            let res = []

            arr.reduce((total, cur, index, arr) => {
                res.push(fn.call(value, cur, index, arr))   // map 中将传入的第二个参数作为回调函数的 this 值
            }, 0)
            return res
        }

        let arr = [1, 2, 3, 4]
        let res = arr.myMap((item) => {
            return item * item
        }) 
        console.log(res);
    </script>

7、实现indexOf

<script>
        String.prototype.myIndexOf = function(s) {
            //方法1:使用正则表达式
            /* let reg = new RegExp(s)
            let res = reg.exec(this) //exec() 方法检索字符串中的指定值。返回值是被找到的值。如果没有发现匹配,则返回 null
            return res === null ? -1 : res.index */

            //方法2:
            let s_l = s.length
            let str_l = this.length
            let index = -1

            if (s_l > str_l) return -1

            for (let i = 0; i <= str_l - s_l; i++) {
                if (this.substr(i, i + s_l) == s) {
                    index = i
                    break
                }
            }
            return index
        }

        //test
        let demo = 'ppap'
        let str = 'a'
        console.log(demo.myIndexOf(str))
    </script>

8、实现Object.assign()

   <script>
        const obj1 = {
            name: 'tou',
            age: 10,
            profile: {
                a: 1
            }
        }

        const obj2 = {
            name: 'kinn',
            age: 8,
            profile: {
                b: 2
            },
            email: 2233
        }

        function assign() {
            if (arguments[1] == null) { // 注意点2
                return arguments[2]
            }
            var to = Object(arguments[1]); // 注意点3
            for (var index = 1; index < arguments.length; index++) {
                var nextSource = arguments[index];
                if (nextSource != null) { // 注意点2
                    // 注意点4
                    for (var nextKey in nextSource) {
                        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                            if (typeof nextSource[nextKey] == 'object') {
                                nextSource[nextKey] = assign({}, to[nextKey], nextSource[nextKey])
                            }
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
            }
            return to;
        }
        console.log(assign({}, obj1, obj2));
        /*输出 {
            name: 'kinn',
            age: 8,
            profile: {
                a: 1,
                b: 2
            },
            email: 2233
        } */
    </script>

<script>
        const obj1 = {
            name: 'tou',
            age: 10,
            profile: {
                a: 1
            }
        }

        const obj2 = {
            name: 'kinn',
            age: 8,
            profile: {
                b: 2
            },
        }

        const obj3 = {
            email: 2233,
            name: 'mile'
        }

        function test(...arg) {
            if (arg[0] == null) {
                return arg[1]
            }
            let to = arg[0]

            for (let i = 1; i <  arg.length; i++) {
                let next = arg[i]
                if (next != null) {
                    for (let key in next) {
                        if (next.hasOwnProperty(key)) {
                            if (typeof next[key] == 'object') {
                                next[key] = test(to[key], next[key])
                            }
                            to[key] = next[key]
                        }
                    }
                }
            }
            return to
        }

        console.log(test({}, obj1));
    </script>

八、图片懒加载

<script>
        let imgs = document.querySelectorAll('img')

        function scrollimg() {
            for (let img of imgs) {
                if (img.offsetTop <= window.innerHeight + document.body.scrollTop) {
                    let data_src = img.getAttribute('data-src')
                    console.log(data_src);
                    img.setAttribute('src', data_src)
                }
            }
        }

        window.addEventListener('scroll', () => {
            scrollimg()
        })

        scrollimg()
    </script>

九、实现一个简单路由

1、hash路由

<body>
    <div id="container">
        <a href="#">首页</a> 
        <a href="#about">关于我们</a>
        <a href="#user">用户列表</a>
    </div>
    <div id="context"></div>
    <script>
        class BaseRouter {
            constructor() {
                this.routes = {};
                this.refresh = this.refresh.bind(this);

                window.addEventListener('load', this.refresh);
                window.addEventListener('hashchange', this.refresh);
            };

            // 对路由进行收集
            route(path, cb) {
                this.routes[path] = cb || function(){};
            };

            

            refresh() {
                const path = `/${window.location.hash.slice(1) || ''}`;
                this.routes[path]();
            }
        }

        const Route = new BaseRouter();

        Route.route('/about', () => changeText("关于我们页面"));
        Route.route('/user', () => changeText("用户列表页"));
        Route.route("/", () => changeText("首页页面"));

        function changeText(arg) {
            document.getElementById('context').innerHTML = arg;
        }
    </script>
</body>

2、history路由

<body>
  <div class="router">
    <a href="./">首页</a>
    <a href="./user">用户列表</a>
    <a href="./about">关于我们</a>
  </div>
  <div class="content"></div>

  <script>
    class BaseRouter {
      constructor () {
        // 路由表
        this.routes = {}
      }

      // 收集路由
      route (path, cb) {
        this.routes[path] = cb || function () {}
      }

      // 访问路由
      go (path) {
        window.history.pushState({path}, null, path)
        const cb = this.routes[path]
        if (cb) cb()
      }
    }
	
	//实例化路由对象
    const Route = new BaseRouter()
    //添加路由
    Route.route('./', () => {changeText('这是首页页面')})
    Route.route('./user', () => {changeText('这是用户页面')})
    Route.route('./about', () => {changeText('这是关于我们页面')})
	
    function changeText (text) {
      let dom = document.querySelector('.content')
      dom.innerHTML = text
    }

    document.querySelector('.router').addEventListener('click', e => {
      if (e.target.tagName === 'A') {
        e.preventDefault()
        Route.go(e.target.getAttribute('href'))
      }
    })
  </script>
</body>

十、 发布订阅模式(event bus)

<script>
        class eventEmitter {
            constructor() {
                this.list = {} //创建事件缓存列表
            }

            //订阅消息
            on(eventName, callBack) {
                    if (!this.list[eventName]) {
                        //当还没有这个事件时,为这个事件添加一个缓存列表
                        this.list[eventName] = [callBack]
                    } else {
                        //当已经存在这个事件的缓存列表之后,直接添加
                        this.list[eventName].push(callBack)
                    }
                }
                //发布消息
            emit(eventName, ...args) {
                    this.list[eventName].forEach(fn => fn.apply(this, args))
                }
                //取消订阅
            remove(eventName, callBack) {
                    this.list[eventName] = this.list[eventName].filter(fn => fn != callBack)
                }
                //只监听一次
            once(eventName, callBack) {
                const fn = () => {
                    callBack()
                    this.remove(eventName, fn)
                }
                this.on(eventName, fn)
            }
        }
    </script>

十一、HTTP请求

1、jsonp

 <script>
        //1、创建回调函数
        function fn(data) {
            console.log(data);
        }
        //创建script元素
        let newScript = document.createElement('script')
            //绑定src
        newScript.src = 'https://www.abc.com?callback=fn'
            //将script元素插入文档
        document.body.appendChild(newScript)
    </script>

2、实现AJAX

(1)简易版

 //get请求
        let xhr = new XMLHttpRequest()
        xhr.open('GET',url,true)
        xhr.onreadystatechange = function(){
            if(xhr.readystate === 4){
                if(xhr.status >= 200 && xhr.status < 300){
                    console.log(xhr.resopnseText);
                }
            }
        }
        xhr.send(null)

        //post请求
        let xhr = new XLMHttpRequest()
        xhr.open('POST',url,true)
        xhr.onreadystatechange = function(){
            if(xhr.readystate === 4){
                if(xhr.status >= 200 && xhr.status < 300){
                    console.log(xhr.responseText);
                }
            }
        }
        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')
        xhr.send(data)

(2)promise实现版

<script>
    function ajax(url, method, data) {
            let xhr = new XMLHttpRequest()
            method = method.toUpperCase()

            let p = new Promise((resolve, reject) => {
                let param = ''
                if (method == 'GET') {
                    if (data instanceof 'object') {
                        let arr = []
                        for (let key in data) {
                            arr.push(`${key}=${data[key]}`)
                        }
                        param = arr.join('&')
                        param = `?${param}`
                    }
                    xhr.open(method, url + param, true)
                } else {
                    xhr.open(method, url, true)
                }
                xhr.onreadystatechange = function() {
                    if (xhr.readystate === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(JSON.parse(xhr.responseText));
                        } else {
                            reject(new ERROR('error'))
                        }
                    }
                }
                if (method == 'GET') xhr.send(null)
                else {
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
                    xhr.send(param)
                }
            })
            return p
        }
</script>

十二、Promise系列

1、promise

    <script>
        const PEDDING = Symbol()
        const FULFILLED = Symbol()
        const REJECTED = Symbol()

        const myPromise = function(fn) {
            this.states = PEDDING
            this.value = ''

            const resolve = (value) => {
                this.states = FULFILLED
                this.value = value
            }

            const reject = (error) => {
                this.states = REJECTED
                this.value = error
            }

            try {
                fn(resolve, reject)
            } catch (error) {
                reject(error)
            }

            this.then = (onFulFilled, onRejected) => {

                if (this.states == FULFILLED) {
                    onFulFilled(this.value)
                }
                if (this.states == REJECTED) {
                    onRejected(this.value)
                }
            }

        }

        //test
        let p = new myPromise((resolve, reject) => {
            resolve(123)
        })
        p.then(value => {
            console.log(value)
        })
    </script>
<script>
        function myPromise(excutor) {
            let self = this
            self.status = 'pending'
            self.value = null
            self.reason = null
            self.onFulfilledCallbacks = []
            self.onRejectedCallbacks = []

            function resolve(value) {
                if (self.status === 'pending') {
                    self.value = value
                    self.status = 'fulfilled'
                    self.onFulfilledCallbacks.forEach(item => item())
                }
            }

            function reject(reason) {
                if (self.status === 'pending') {
                    self.reason = reason
                    self.status = 'rejected'
                    self.onRejectedCallbacks.forEach(item => item())
                }
            }
            try {
                excutor(resolve, reject)
            } catch (err) {
                reject(err)
            }
        }


        myPromise.prototype.then = function(onFulfilled, onRejected) {
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(data) {
                resolve(data)
            }
            onRejected = typeof onRejected === 'function' ? onRejected : function(err) {
                throw err
            }
            let self = this
            if (self.status === 'fulfilled') {
                return new myPromise((resolve, reject) => {
                    try {
                        let x = onFulfilled(self.value)
                        if (x instanceof Promise) {
                            x.then(resolve, reject)
                        } else {
                            resolve(x)
                        }
                    } catch (err) {
                        reject(err)
                    }
                })
            }
            if (self.status === 'rejected') {
                return new myPromise((resolve, reject) => {
                    try {
                        let x = onRejected(self.reason)
                        if (x instanceof myPromise) {
                            x.then(resolve, reject)
                        } else {
                            resolve(x)
                        }
                    } catch (err) {
                        reject(err)
                    }
                })
            }
            if (self.status === 'pending') {
                return new myPromise((resolve, reject) => {
                    self.onFulfilledCallbacks.push(() => {
                        let x = onFulfilled(self.value)
                        if (x instanceof myPromise) {
                            x.then(resolve, reject)
                        } else {
                            resolve(x)
                        }
                    })
                    self.onRejectedCallbacks.push(() => {
                        let x = onRejected(self.reason)
                        if (x instanceof myPromise) {
                            x.then(resolve, reject)
                        } else {
                            resolve(x)
                        }
                    })
                })
            }
        }

        myPromise.prototype.catch = function(fn) {
            return this.then(null, fn)
        }

        let p = new myPromise((resolve, reject) => {
            resolve(2233)
        })

        p.then((data) => {
            p = new myPromise((res, rej)=>{
                
            })
        }).then((data) => {
            console.log(data);
        })
    </script>


如上代码可以说明p1.then()的结果是一个与p1不同的promise对象。

 

换句话说,then()会封装一个全新的promise对象p2。那既然 p2也是一个promise对象,那么,p2的状态(promiseStatus)和值(promiseValue)分别是什么?

 

规则如下:

如果p1的状态是pending,则p2的状态也是pending。

•如果p1的状态是resolved,then()会去执行f_ok,则p2的状态由f_ok的返回值决定。

•如果f_ok返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_ok函数的return值。•如果f_ok返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。•如果f_ok这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。

如果p1的状态是rejected,then()会去执行f_err,则p2的状态由f_err的返回值决定。

•如果f_err返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_err函数的return值。•如果f_err返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。

•如果f_err这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。

2、promise.all

    <script>
        // !! 判断变量a为非空,未定义或者非空串才能执行方法体的内容
        function isPromise(obj) {
            return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
        }

        function myPromiseAll(arr) {
            let res = []
            return new Promise((reslove, reject) => {
                for (let i = 0; i < arr.length; i++) {
                    if (isPromise(arr[i])) {
                        arr[i].then(data => {
                            res[i] = data
                            if (res.length == arr.length) {
                                reslove(res)
                            }
                        }).catch(error => {
                            reject(error)
                        })
                    } else {
                        res[i] = arr[i]
                    }
                }
            })
        }

        //test
        var p1 = Promise.resolve('a');
        var p2 = Promise.resolve('b');
        var p3 = Promise.resolve('c');
        /* Promise.all([p1, p2, p3]).then(function(value) {
            console.log(value);
        }) */

        myPromiseAll([p1, p2, p3]).then(data => console.log(data))
    </script>

3、promise.race

<script>
        function myPromiseRace(arr) {
            return new Promise((resolve, reject) => {
                for (let i = 0; i < arr.length; i++) {
                    return arr[i].then(resolve, reject)
                }
            })
        }

        //test 
        const p1 = Promise.reject('a');
        const p2 = Promise.resolve('b');
        const p3 = Promise.resolve('c');

        /* Promise.race([p1, p2, p3]).then(value => {
            console.log(value);
        }).catch(error => {
            console.log(error);
        }) */

        myPromiseRace([p1, p2, p3]).then(value => {
            console.log(value);
        }).catch(error => {
            console.log(error);
        })
    </script>

4、promise经典题

setTimeout(() => {
            console.log(0); //9
        }, 0);
        new Promise((resolve, reject) => {
            console.log(1); // 1
            resolve()
        }).then(() => {
            console.log(2); //3
            new Promise((resolve, reject) => {
                console.log(3); //4
                resolve()
            }).then(() => {
                console.log(4); //6
            }).then(() => {
                console.log(5); //8
            })
        }).then(() => {
            console.log(6); //7
        })
        new Promise((resolve, reject) => {
                console.log(7); //2
                resolve()
            }).then(() => {
                console.log(8); //5
            })
            // 1 7 2 3 8 4 6 5 0

// then() 里面放入的是函数就会放进队列中等待执行,下一个then中的内容,得等上一个then中状态转变才会放入队列中

    <script>
        new Promise((res, rej) => {
            console.log(1) //1
            res()
        }).then(() => {
            console.log(2) //2
            return new Promise((res, rej) => { //没有return 就变成123465
                console.log(3) //3
                res()
            }).then(() => {
                console.log(4) //4
            }).then(() => {
                console.log(5) //5
            })
        }).then(() => {
            console.log(6) //6
        })

        //1 2 3 4 5 6
    </script>

十三、链表

1、单链表

    <script>
        //节点类
        function node(element) {
            this.element = element
            this.next = null
        }

        //链表类
        class LinkedList {
            constructor() {
                this.head = null;
                this.length = 0; // length 同数组 length 与下标关系
            }

            // 追加元素
            append(element) {
                let node = new Node(element);
                let current = null; // 指针?

                if (this.head === null) {
                    this.head = node;
                } else {
                    current = this.head;
                    while (current.next) {
                        current = current.next;
                    }
                    current.next = node;
                }
                this.length++;
            }

            // 任意位置插入元素
            insert(position, element) {
                if (position >= 0 && position <= this.length) {
                    let node = new Node(element);
                    let current = this.head;
                    let previous = null;
                    let index = 0;
                    if (position === 0) {
                        node.next = current
                        this.head = node;
                    } else {
                        while (index++ < position) {
                            previous = current;
                            current = current.next;
                        }
                        node.next = current;
                        previous.next = node;
                    }
                    this.length++;
                    return true
                }
                return false
            }

            // 移除指定位置元素
            removeAt(position) {
                if (position > -1 && position < length) {
                    let current = this.head;
                    let previous = null;
                    let index = 0;
                    if (position === 0) {
                        this.head = current.next;
                    } else {
                        while (index++ < position) {
                            previous = current;
                            current = current.next;
                        }
                        previous.next = current.next;
                    }
                    this.length--;
                    return current.element;
                }
                return null
            }

            // 寻找元素下标
            findIndex(element) {
                let current = this.head;
                let index = -1;
                while (current) {
                    if (element === current.element) {
                        return index + 1;
                    }
                    index++;
                    current = current.next;
                }

                return -1;
            }

            isEmpty() {
                return !this.length;
            }

            size() {
                return this.length;
            }
        }
    </script>

2、双向链表

    <script>
        class Node {
            constructor(element) {
                this.element = element;
                this.prev = null;
                this.next = null;
            }
        }

        // 双向链表
        class DoubleLinkedList {
            constructor() {
                this.head = null;
                this.tail = null;
                this.length = 0;
            }

            // 任意位置插入元素
            insert(position, element) {
                if (position >= 0 && position <= ehis.length) {
                    let node = new Node(element);
                    let current = this.head;
                    let previous = null;
                    this.index = 0;
                    // 首位
                    if (position === 0) {
                        if (!head) {
                            this.head = node;
                            this.tail = node;
                        } else {
                            node.next = current;
                            this.head = node;
                            current.prev = node;
                        }
                    } else if (position === this.length) { // 末尾
                        current = this.tail;
                        current.next = node;
                        node.prev = current;
                        this.tail = node;
                    } else { // 中间
                        while (index++ < position) {
                            previous = current;
                            current = current.next;
                        }
                        node.next = current;
                        previous.next = node;
                        current.prev = node;
                        node.prev = previous;
                    }
                    this.length++;
                    return true;
                }
                return false;
            }

            // 移除指定位置元素
            removeAt(position) {
                if (position > -1 && position < this.length) {
                    let current = this.head;
                    let previous = null;
                    let index = 0;

                    // 首位
                    if (position === 0) {
                        this.head = this.head.next
                        this.head.prev = null
                        if (this.length === 1) {
                            this.tail = null
                        }
                    } else if (position === this.length - 1) { // 末位
                        this.tail = this.tail.prev
                        this.tail.next = null
                    } else { // 中位
                        while (index++ < position) {
                            previous = current
                            current = current.next
                        }
                        previous.next = current.next
                        current.next.prev = previous
                    }
                    this.length--;
                    return current.element;
                } else {
                    return null;
                }
            }
        }
    </script>

十四、数据双向绑定

(1)vue2.0

<input type="text" name="" id="">
    <script>
        let input = document.querySelector('input')
        data = {}

        Object.defineProperty(data, 'text', {  //'text'表示监视的属性
                // 数据变化 —> 修改视图
                set(newVal) {
                    input.value = newVal
                        // console.log('input');
                },
                get() {
                    return input.value
                }
            })
            // 视图更改 --> 数据变化
        input.addEventListener('keyup', function(e) {
            data.text = e.target.value
            console.log(data.text);
        })
    </script>

(2)proxy双向数据绑定

<body>
    <input type="text" id="input">
    <span id="span"></span>
    <script>
        const input = document.getElementById('input')
        const span = document.getElementById('span')

        //设置代理对象
        let obj = {}

        let inputProxy = new Proxy(obj, {
            //配置代理选项
            get: function(target, key) {
                return target[key]
            },
            set: function(target, key, value) {
                if (key === 'text' && value) {
                    target[key] = value
                    span.innerHTML = value
                }
            }
        })

        //添加事件
        input.addEventListener('keyup', function(e) {
            inputProxy.text = e.target.value
            console.log(inputProxy.text);
        })
    </script>
</body>

十五、判断两个对象是否相等

 <script>
        function isObjectEqual(obj1, obj2) {
            let o1 = obj1 instanceof Object;
            let o2 = obj2 instanceof Object;
            if (!o1 || !o2) { // 如果不是对象 直接判断数据是否相等
                return obj1 === obj2
            }
            // 判断对象的可枚举属性组成的数组长度
            if (Object.keys(obj1).length !== Object.keys(obj2).length) {
                return false;
            }
            for (let attr in obj1) {
                let a1 = Object.prototype.toString.call(obj1[attr]) == '[object Object]'
                let a2 = Object.prototype.toString.call(obj2[attr]) == '[object Object]'
                let arr1 = Object.prototype.toString.call(obj1[attr]) == '[object Array]'
                if (a1 && a2) {
                    // 如果是对象继续判断
                    if (!isObjectEqual(obj1[attr], obj2[attr])) {
                        return false
                    }
                } else if (arr1) {
                    // 如果是对象 判断
                    if (obj1[attr].toString() != obj2[attr].toString()) {
                        return false;
                    }
                } else if (obj1[attr] !== obj2[attr]) {
                    // 不是对象的就判断数值是否相等
                    return false
                }
            }
            return true
        }

        let obj1 = {
            name: 'amy',
            eat: {
                body: 1,
                say: 'kk'
            },
            time: [1, 2, 3, 4, 5]
        }
        let obj2 = {
            name: 'amy',
            eat: {
                body: 1,
                say: 'kk'
            },
            time: [1, 2, 3, 4, 5],
        }
        console.log(isObjectEqual(obj1, obj2))
    </script>

十六、判断一个对象是空对象

 <script>
        function test(obj) {
            //(1) JSON.stringify
            // if (JSON.stringify(obj) === '{}'){
            //     return true
            // }

            //(2)使用Object.keys()
            if (Object.keys(obj).length == 0) {
                return true
            }
            return false
        }

        console.log(test({
            name: 'mile'
        }));
    </script>

十七、实现函数判断类型的函数

function getType(obj) {
   if (obj === null) return String(obj);
   return typeof obj === 'object' 
   ? Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '').toLowerCase()
   : typeof obj;
}

// 调用
getType(null); // -> null
getType(undefined); // -> undefined
getType({}); // -> object
getType([]); // -> array
getType(123); // -> number
getType(true); // -> boolean
getType('123'); // -> string
getType(/123/); // -> regexp
getType(new Date()); // -> date

十八、数据绑定基本实现

题目:
// 实现一个方法,可以给 obj 所有的属性添加动态绑定事件,当属性值发生变化时会触发事件
let obj = {
  key_1: 1,
  key_2: 2
}
function func(key) {
  console.log(key + ' 的值发生改变:' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; // 此时自动输出 "key_1 的值发生改变:2"
obj.key_2 = 1; // 此时自动输出 "key_2 的值发生改变:1"

答:
function bindData(obj, fn) {
  for (let key in obj) {
    Object.defineProperty(obj, key, {
      set(newVal) {
        if (this.value !== newVal) {
          this.value = newVal;
          fn.call(obj, key);
        }
      },
      get() {
        return this.value;
      }
    })
  }
}

十九、有一个祖先树状 json 对象,当一个人有一个儿子的时候,其 child 为其儿子对象,如果有多个儿子,child 为儿子对象的数组。

请实现一个函数,找出这个家族中所有有多个儿子的人的名字(name),输出一个数组。

// 样例数据
let data = {
  name: 'jack',
  child: [
    { name: 'jack1' },
    {
      name: 'jack2',
      child: [{
        name: 'jack2-1',
        child: { name: 'jack2-1-1' }
      }, {
        name: 'jack2-2'
      }]
    },
    {
      name: 'jack3',
      child: { name: 'jack3-1' }
    }
  ]
}

答:
function findMultiChildPerson(data) {
  let nameList = [];

  function tmp(data) {
    if (data.hasOwnProperty('child')) {
      if (Array.isArray(data.child)) {
        nameList.push(data.name);
        data.child.forEach(child => tmp(child));
      } else {
        tmp(data.child);
      }
    }
  }
  tmp(data);
  return nameList;
}

二十、js继承

<script>
        //1、原型链继承
        //(1)所有子类共用一个父类实例,任何一个子类的来自父类的引用对象改变,会影响所有子类
        //(2)无法向父类构造函数传参
         function Father() {
             this.name = 'mile'
         }

         Father.prototype.getName = function() {
             console.log(123);
         }

         function Son() {
             this.age = 19
         }

         Son.prototype = new Father()
         Son.prototype.constructor = Son

         let son = new Son()
         console.log(son.name);
         console.log(son.age);
         son.getName() 

        //2、构造函数继承
        //(1)能向父类传参,多个子类不共享一个原型实例
        //(2)不能使用父类原型上的方法
         function Father() {
            this.name = 'mile'
        }

        Father.prototype.getName = function() {
            console.log(123);
        }

        function Son() {
            this.age = 19
            Father.call(this, arguments)
        }

        Son.prototype.constructor = Son
        let son = new Son('kinn')
        console.log(son.name);
        console.log(son.age);
        son.getName() //会报错
        

        //3、组合式继承
        //(1)能向父类传参,多个子类不共享一个原型实例
        //(2)能使用父类原型上的方法
        //(3)但是每次创建实例时会调用两次父类构造函数(new Father(), Father.call())
        function Father() {
            this.name = 'mile'
        }

        Father.prototype.getName = function() {
            console.log(123);
        }

        function Son() {
            this.age = 19
            Father.call(this, ...arguments)
        }

        Son.prototype = new Father()
        Son.prototype.constructor = Son
        let son = new Son('kinn')
        console.log(son.name);
        console.log(son.age);
        son.getName() 

        //4、寄生式继承
        //将指向父类实例改为指向父类原型,从而减少一次构造函数的执行
        function Father() {
            this.name = 'mile'
        }

        Father.prototype.getName = function() {
            console.log(123);
        }

        function Son() {
            Father.call(this, ...arguments)
            this.age = 18
        }

        Son.prototype = Object.create(Father.prototype)
        Son.prototype.constructor = Son
        let son = new Son('kinn')
        console.log(son.name);
        console.log(son.age);
        son.getName()
    </script>

<think>嗯,用户想找关于的解答或练习,特别是针对IT面试中的算法和数据结构。首先,我需要理解“”在这里指的是什么。根据上下文和引用内容,应该是面试中需要现场编写代码的目,比如白板编程或者在线编码测试。用户可能正在准备面试,尤其是大厂的面试,所以需要相关的资源和方法来提高代码的能力。 接下来,用户提到的引用资料中有几个关键点。引用[1]提到大厂面试中数据结构的基本操作和实际应用,引用[2]强调面试代码的规律和技巧,引用[3]则具体到链表等高频算法。这说明用户需要具体的练习目、解答方法以及应对策略。 用户的需求可能包括几个方面:如何有效练习代码、常见的高频目有哪些、如何分析解思路、优化代码的方法,以及推荐的资源。需要将这些内容结构化,给出清晰的步骤和实例,帮助用户逐步提升。 然后,我需要考虑如何组织回答。首先介绍的考察重点,然后分步骤讲解如何准备,包括高频目类型、解方法、代码规范等。接着提供具体的例子,比如链表的反转、快速排序的实现,以及动态规划的问,每个例子附上代码和解析。之后,给出优化技巧和推荐的练习资源,最后生成相关问引导用户深入思考。 需要注意的是,回答要符合用户的要求:使用中文,行内公式用$...$,独立公式用$$...$$,正确使用LaTeX语法,并在引用处添加标识。例如,在提到大厂面试时引用[^1],在讨论代码规律时引用[^2],在链表目部分引用[^3]。 此外,用户可能希望得到实战性强的建议,比如分步骤的练习方法和具体目示例。需要确保每个部分都紧扣主,提供可操作的建议,并引用相关资源增强可信度。同时,生成的“相关问”部分应该引导用户进一步探索,覆盖性能分析、优化方法、应用场景等方面,这样能帮助用户全面掌握知识点。 最后,检查回答是否符合格式要求,确保代码块使用正确的语法高亮,引用标识正确,没有使用Markdown格式,并且内容结构清晰,逻辑连贯。这样用户能够轻松跟随,有效提升面试准备效率。</think>### 代码面试的解答与练习指南 #### 一、的考察重点 代码在IT面试中主要考察以下能力: 1. **数据结构与算法基础**:如链表操作(反转、合并)、二叉树遍历(前序/中序/后序)、堆栈队列应用等 2. **代码实现规范性**:边界条件处理、变量命名规范、异常输入处理 3. **时间复杂度优化**:能否通过空间换时间或数学规律简化问,如动态规划问$O(n^2) \rightarrow O(n)$ 4. **问拆解能力**:将复杂问分解为可执行步骤,如实现LRU缓存需要组合哈希表+双向链表 #### 二、高效练习方法 (1)**高频型专项突破** ```python # 示例1:链表反转(高频考点) class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def reverse_list(head: ListNode) -> ListNode: prev, curr = None, head while curr: next_node = curr.next curr.next = prev prev = curr curr = next_node return prev ``` (2)**代码模板整理** - 快慢指针模板(环形链表检测) - 回溯法模板(全排列问) - 滑动窗口模板(最长无重复子串) (3)**复杂度分析实践** 对于二分查找算法: $$ T(n) = T(n/2) + O(1) $$ 通过主定理可得时间复杂度为$O(\log n)$ #### 三、典型目解析 **目**:实现快速排序(大厂常考排序算法) ```python def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr)//2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) ``` *关键点*:基准值选择、分区策略、递归终止条件 **进阶优化**:当数组基本有序时,随机选择pivot避免$O(n^2)$最坏情况 #### 四、推荐练习资源 1. 《剑指Offer》经典解(覆盖90%面试型) 2. LeetCode热门企业库(按字节跳动、腾讯等公司分类) 3. 动态规划专项训练(背包问、股票买卖问) 4. 系统设计高频(设计推特、设计缓存系统) #### 五、应试技巧 1. **5分钟思考法**:先厘清问边界,再动编码 2. **测试用例设计**:显式验证空输入、极端值、重复元素等情况 3. **代码评审模拟**:完成后解释时间/空间复杂度及优化思路
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值