综合面试题-js基础

综合面试题-js基础

call和apply的区别是什么,哪个性能更好

都是用来改变this指向的,区别是传参

call是一个一个逗号分隔传参,apply是以一个数组的形式去传参,所有参数以一个数组的形式传递进去

3个以内,两者差不多

超过3个,call比apply性能好一些

其他相关:都是function原型上的方法,供每个function实例调用。

bind并没有立即执行,而是预先处理保存了参数信息,改变this。主动引出bind概念,主动引出原型和原型链、实例和类。

这样最多也只能精确到毫秒,有更专业的方法

console.time('START')
for(let i=0;i<1000000000;i++){


}
console.timeEnd('START')

性能测试-测时间

(5).add(3).minus(2)

类和实例 在类的原型上构建方法并且能够实现链式写法的

  1. 实例调取方法,把方法放到当前所属类的原型上

  2. 链式调用:return this。返回结果为当前类的实例

Number.prototype.add=function(n){
    // if(typeof n !== 'number' && !(n instanceof Number)) return;
    if(!(typeof n === 'number' || n instanceof Number)) return this;
    return this+n;
}
Number.prototype.minus=function(n){
    if(typeof n !== 'number' && !(n instanceof Number)) return this;
    return this-n;
}
console.log((5).add(3).minus(2));

(function(){
    // 处理参数的容错性
    function check(n){
        n=Number(n);
        return isNaN(n)?0:n;
        // return n=Number(n) && isNaN(n)?0:n; // 其实这样n没改
        // 测试
        console.log(n=Number(n));
        console.log(n);
        // console.log(bool);
    }
    check('3');
    function add(n){
        n=check(n);
        return this+n;
    }
    function minus(n){
        n=check(n);
        return this-n;
    }
    ['add','minus'].forEach(item=>{
        // 字符串eval,转换成一个js表达式=>代表的就是当前函数
        Number.prototype[item]=eval(item);
    })
    


})();
// 不能直接5.add,得用小括号()包起来
console.log((5).add(3).minus(2));

箭头函数和普通函数的区别

语法上更加简洁:

小括号,箭头,大括号;方法中函数体中只有一行代码把大括号省略return省略,不需要function,只有一个参数时小括号不用加

箭头函数中的this没有自己的this,是它上级作用域中的this;只跟上下文有关系,使用call apply无法改变

没有arguments,基于...args获取传递的参数。而且args本身就是个数组。(其实箭头函数比普通函数都要好那么一点点)

不能被new执行。没有prototype

注意:...args不是箭头函数独有的

function fn(a,...args){
    console.log(a);
    console.log(args);
}


fn(3,8,9);
/**
 * 1.语法更简洁
 */
let fnB=function(x){
    return function(y){
        return x+y;
    }
}
let fnA=x=> y=>x+y;
console.dir(fnB)
console.dir(fnA)
/**
 * 2.所处上下文中的this
 */
let obj={name:'obj1'}
function fn1(){
    console.log(this);
}
let fn2=()=>{
    console.dir(this)
}
fn1.call(obj);
fn2.call(obj);


document.body.onclick=function(){
    //this:body
    let arr=[1,3,3]
    // sort中 this:arr
    arr.sort(function(a,b){
        // 回调函数 this:window
        return a-b;
    })
    arr.sort((a,b)=>a-b)
}


function each(arr,callBack){
    for(let i=0;i<arr.length;i++){


        // let flag=callBack(arr[i],i);
        // 函数执行前面没点,所以this默认是window
        // 这样改变callBack中的this
        let flag=callBack.call(arr,arr[i],i);
        // 这里还能支持返回值结束循环
        if(!flag) break;
    }
}
each([10,20,30,40],function(item,index){
    return true;
})

扩展一下回调函数

把函数当作实参传给另一个方法,在另一个方法中会执行你这个回调

  1. 传递异步操作的结果,外部的回调函数就能接收到

  2. 函数执行一定会有返回值,内部能接收到回调的返回值

思考题

each

Array.prototype.each=function(callBack,obj){
    obj=obj || window;
    for(let i=0;i<this.length;i++){
        if(callBack.call(obj,arr[i],i)===false){
            break;
        }
        this[i]=callBack.call(obj,arr[i],i);
    }
}


let arr=[10,20,30,'AA',40]
arr.each(function(item,index){
    if(isNaN(item)){
        return false;
    }
    return item*10;
},{name:'obj'})
console.log(arr);

*replace重写

用exec

let str=`<a href=http://www.baidu.com></a>
<p>ppppppppppppppppppppppppppppp</p>
<a href=http://www.bilibili.com></a>
<span>pppppppppppppppppppppppp</span>`
let reg=/<a href=(.*)>.*<\/a>/g
console.log(str.match(reg));
// console.log(str.matchAll(reg));
console.log(str.replace(reg,function(...args){
    console.log(args)
    return 'xxx'
}))
String.prototype.replaceMy=function(reg,callback){
    let mat=null;
    let matInfo=[]
    let res=''
    while(mat=reg.exec(this)){
        /**
         * exec的结果是一个数组
         * 0:匹配到的所有内容
         * 1:分组1
         * 2:分组2
         * ......
         */
        console.log('mat===');
        console.log(mat);
        // 1.回调中就可以拿到匹配的完整内容和下面的分组
        res=callback(...mat);
        // 2.替换
        // 存储信息
        matInfo.push({
            index:mat.index,
            context:mat[0],
        })
    }
    // 这里稍微有一点算法
    let pos=0;
    let strNew=''
    for(let i=0;i<matInfo.length;i++){
        let {index,context}=matInfo[i];
        for(;pos<index;pos++){
            strNew+=this[pos]
        }
        strNew+=res;
        pos+=context.length;
    }
    let lastInfo=matInfo[matInfo.length-1];
    const {index,context}=lastInfo;
    let last=index+context.length
    if(last<this.length){
        for(;last<this.length;last++){
            strNew+=this.charAt(last);
        }
    }
    return strNew;
}
console.log(str.replaceMy(reg,function(...args){
    return 'xxx'
}))


大小写取反

  1. toUpperCase之后是否等于自己

  2. 通过码值判断

let str='wetest QQ net 云真机';
str=str.replace(/[a-zA-Z]/g,function(context){
    if(context.toUpperCase()===context){
        return context.toLowerCase()
    }
    return context.toUpperCase();
})
console.log(str);

用正则更简洁

字符串匹配

循环方法

// 业务逻辑实现
let s='abcdefghijklmn'
let t='aaa';
let len=t.length;
let flag=true;
for(let i=0;i<s.length-len+1;i++){
    flag=true;
    for(let j=0;j<len;j++){
        if(s[i+j]!==t[j]){
            flag=false;
            break;
        }
    }
    if(flag) break;
}
if(flag){
    console.log('存在')
}else{
    console.log('不存在')
}


(function(){
    String.prototype.myIndexOf=function(t){
        // this:s t
        let lenS=this.length;
        let lenT=t.length;
        let index=-1;
        // 如果原始字符串长度小,直接返回-1
        if(lenS<lenT) return index;
        // 这里要+1
        for(let i=0;i<lenS-lenT+1;i++){
            // 这里用了字符串方法slice
            if(this.slice(i,i+lenT)===t){
                index=i;
                break;
            }
            // 每次循环前index赋值为i
            // index=i;
            // for(let j=0;j<len;j++){
            //     if(this[i+j]!==t[j]){
            //         index=-1;
            //         break;
            //     }
            // }
            // if(index>-1) break;
            
        }
        return index;
    }
    let i='wetest try perfdog'.myIndexOf('try');
    console.log(i);
    i='wetest try perfdog'.myIndexOf('my');
    console.log(i);






})();


正则方法

  • 把一个变量变成正则,需要new RegExp的方式

  • 或者用eval

(function(){
    String.prototype.myIndexOf=function(t){
        // this:s t
        // let reg=eval(`/${t}/`);
        let reg=new RegExp(t);
        let res = reg.exec(this);
        return res?res.index:-1;
        
    }
    let i='wetest try perfdog'.myIndexOf('try');
    console.log(i);
    i='wetest try perfdog'.myIndexOf('my');
    console.log(i);


    console.log(/a/.test('abcdef'));




})();


​​​​​​​对象的属性值

  1. 字符串100数字100是一个属性

  2. 对象属性会自动调toString方法转换成字符串[object Object],所以也永远是一个属性

(function(){
    var a={},b='123',c=123;
    a[b]='b'
    a[c]='c'
    console.log(a);
    console.log(a[b]);
})();
(function(){
    var a={},b=Symbol('123'),c=Symbol('123');
    a[b]='b';
    a[c]='c';
    console.log(a);
    console.log(a[b]);
})();
(function(){
    var a={},b={key:123},c={key:456};
    a[b]='b';
    a[c]='c';
    // 遇到对象属性名会默认转换成字符串
    // 这个是toString是做类型检测的
    console.log('toString===')
    console.log(Object.prototype.toString.call(b));
    console.log(Array.prototype.toString.call([11,22]));
    
    console.log(a);
    console.log(a[b]);
})();

​​​​​​​用正则检测URL

域名的写法:什么什么点,什么点。可能3组可能4组

出现的是,数字字母下划线,加上中杠

www.baidu.com

wetest.qq.comkbs.sports.qq.com

// 项目中的做法
export function isUrl(url: string) {
    return /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?/.test(url);
}

let url='https://www.baidu.com/index.html?query=abc#href';
let reg=/http(s)?:\/\/([\w-]+\.)+[a-z0-9]+(\/[\w./%-?#&=])?/;
let reg1=/(http(?:s)?:\/\/)((?:[\w-]+\.)+[a-z0-9]+)((?:\/[^/?#]+)+)?(\?[\w-&=%]+)?(#[\w-%]+)?/
console.log(reg.test(url));
console.log(reg1.exec(url));


正则相关

我要知道这是第几个分组,只要从左到右数第几个括号就行

?: 非捕获分组

​​​​​​构造函数

function Foo(){
    Foo.a=function(){
        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();

​​​​​​​编写代码实现图片的懒加载

背景原因

为什么要做图片懒加载:

加载css,加载html,加载js,从服务器拿到数据完成数据绑定,

其实每一张图片都是属于比较大的资源;几MB,十几KB,二十几KB;其实每一张图片就跟一个css一个js差不多……其实用户只看到一屏,图片延后加载是非常重要的,优化页面性能

加载分析

  1. 页面加载完,第一屏的时候

$(document).ready: 这个指的是dom结构加载完

$(window).onload: 整个页面资源加载完。当页面所有资源都加载完成时候

这2个是不一样的要注意区分

  1. 鼠标滚轮滚动的时候

图片加载的条件,当图片完全出现在视野中

这里有2个概念,浏览器和页面body;

滚动的时候页面会往上走,图片距离页面body顶端的距离是不变的,这个不会随着你怎么动而改变的;图片的位置一定A的值就定下来了;dom:offset

B:浏览器底边距离页面顶端的偏移

$A: imgBox.offsetHeight+offsetTop,自身的高度+距离父级的上偏移

$B: document.documentElement.clientHeight 一屏的高度 + 滚动条卷去的高度

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" crossorigin="anonymous"></script><script></script>
    <style>
        .container{
            margin-top: 1000px;
        }
        /*
        通常让.imgBox与img宽高比例保持一致,避免图片变形
         */
        .imgBox{
            background-color: #adb5bd;
            width: 867px;
            height: 181px;
            margin-bottom: 30px;
        }
        img{
            display: none;
            width: 100%;
        }
    </style>
</head>
<body>
    <div class="container">
<!--        <div class="imgBox">-->
<!--            <img src="" alt="" data-src="https://zhufengpeixun.oss-cn-beijing.aliyuncs.com/banner022.png">-->
<!--        </div>-->
<!--        <div class="imgBox">-->
<!--            <img src="" alt="" data-src="https://zhufengpeixun.oss-cn-beijing.aliyuncs.com/banner022.png">-->
<!--        </div>-->
<!--        <div class="imgBox">-->
<!--            <img src="" alt="" data-src="https://zhufengpeixun.oss-cn-beijing.aliyuncs.com/banner022.png">-->
<!--        </div>-->
    </div>


    <script>
        let str=new Array(20).fill('<div class="imgBox">\n' +
            '            <img src="" alt="" data-src="https://zhufengpeixun.oss-cn-beijing.aliyuncs.com/banner022.png">\n' +
            '        </div>').join('')
        document.querySelector('.container').innerHTML=str;
        let $imgBoxs=$('.container .imgBox');
        let $window=$(window);
        $window.on('scroll',function () {
            let $B=$window.outerHeight()+$window.scrollTop();
            Array.prototype.forEach.call($imgBoxs,box=>{
                // box是dom对象需要$(...)一下
                let $img=$(box).children('img');
                if($img.attr('isloading')) return;
                console.log('ok');
                // offsetHeight + offsetTop
                let $A=$(box).outerHeight()+$(box).offset().top;
                // document.documentElement.clientHeight + scrollY
                if($A<=$B){
                    $img.attr('src',$img.attr('data-src'));
                    $img.on('load',function () {
                        // $img.css('display','block');
                        // $img.show(2000);
                        $img.fadeIn("slow");
                    })
                    $img.attr('isloading',1)
                }
            })


        })
    </script>
</body>
</html>


正则包含大小写和数字

正向预查:其实就是条件

// 突然发现正则很灵活,单个字符是A或者a
let reg=/(?!^[A-Za-z]+$)(?!^[A-Z0-9]$)(?!^[a-z0-9]$)^([a-zA-Z0-9]{6,16})$/
console.log(reg.exec('AAAaaa111'))
console.log(reg.exec('AAAaaa'))
console.log(reg.exec('AAAAAA'))
console.log(reg.exec('aaaaaa'))
console.log(reg.exec('111111'))
console.log(reg.exec('AAA111'))
console.log('-------------------------')
reg=/(?!^[0-9a-zA-Z]+$)^[0-9a-zA-Z-]{1,10}$/
console.log(reg.exec('aaaaaaa'))
console.log(reg.exec('aaaaaaa---'))

加上了^...$:就表示只能是这个

这个点是有点晕:后面加了^$的,前面(?=)里也要加

非必须,?=表示的就是包含;

必须,?!排除掉一些情况;

​​​​​​​遍历属性选择器

  1. 首先获取页面上所有的标签

  2. 然后再循环每个元素去判断attribute

function $attr(property,value){
    // 所有元素节点
    let elements=document.getElementsByTagName('*');
    let arr=[];
    Array.prototype.forEach.call(elements,elem=>{
        if(property==='class'){
            let reg=new RegExp(`\\b${value}\\b`,'g')
            if(reg.test(elem.getAttribute(property))){
                arr.push(elem);
            }
        }else if(elem.getAttribute(property)===value){
            arr.push(elem)
        }
    })
    return arr;
}
$attr('class','box');

正则给英文单词加空格

let reg=/\b[a-zA-Z]+\b/g;
let str='big大花园dog'
str=str.replace(reg,function(val){
    return ` ${val} `;
})
console.log(str);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值