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

目录

一、解析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>

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
华为撕题leetcode是指华为面试中要求候选人现场撕leetcode题目。这种面试方式常用于评估候选人的编程能力和解决问题的能力。 使用华为撕题leetcode的优点是能够直接考察候选人的编程能力和思维逻辑。面试官可以根据候选人的解题过程和代码实现来评估其算法和编程能力,从而更直观地了解候选人的能力水平。 使用这种面试方式还可以考察候选人在时间有限的情况下解决问题的能力。撕编码题要求候选人在短时间内完成编写算法和实现代码。这种情况下,候选人需要通过灵活运用算法知识、代码实现技巧和系统思考能力,快速找到解决问题的方法。 然而,华为撕题leetcode也存在一定的局限性。一方面,撕题leetcode可能只考察候选人的算法和编程能力,而忽略了其他的技术要求,如系统设计、代码架构等。另一方面,由于题目限定在leetcode的题库中,候选人可能通过事先刷题来准备,从而提前得到题目的答案,影响面试评估的公正性。 为了克服这些限制,华为还会在面试中结合其他的问题和项目经验,综合评估候选人的整体能力。此外,华为还会要求候选人进行coding test,即在指定时间内以线上编码的方式解决问题,更直观地了解候选人的编码能力。 总的来说,华为撕题leetcode是一种评估候选人编程能力和解决问题能力的有效方式,但也需要综合考虑其他方面的能力和筛选方法,以评估候选人的真实水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值