前端面试题01

1. div快速居中方法?

父级元素display:flex;
子级元素margin:auto;

2. padding和margin的区别?

作用对象不同
padding:改变盒子本身大小(作用于自身)
margin: 不改变盒子大小(作用于外部)

3. vw和百分比有什么区别?

vw不继承和设备的宽度有关系
百分比有继承和父级宽度有关

4. 如何让谷歌浏览器支持小字体?

缩放

transform:scale(0.8);
-webkit-transform:scale(0.8);  //是针对webkit内核的浏览器(如Chrome和Safari)的私有属性前缀,以确保在这些浏览器中也能正确显示缩放效果

5. let和var的区别?

var 有变量提升 =>先上车后买票

console.log(name)  //输出为:小明同学   并非:undefined
var name = "小明同学"

var 没有局部作用域 =>红杏出墙

function fn2() {
for(var i=0;i<5;i++) {

}
console.log(i)    //5

var 存在声明覆盖 =>套牌车

var name = "大郎"
var name = "西门官人"
console.log(name)  //西门官人

let反之 使用let更规范

6. 深拷贝和浅拷贝

### //1.基本数据类型
//这属于赋值和拷贝没有关系 在内存中建立了自己单独的储存空间
//必须区分的话属于深拷贝
let a = 5;
let b = a;
b= 3;
console.log(a,b) // 5,3

### //2.数组与对象的赋值叫做浅拷贝=>藕断丝连
let arr= [1,2,3];
let newArr=arr;
newArr.push(4)
console.log(arr,newArr) //1,2,3,4  1,2,3,4

### //3.解构赋值是深拷贝还是浅拷贝
#### //3.1解构赋值针对*一维数组*可以认为是深拷贝
let arr=[1,2,3];
let newArr = [...arr];
newArr.push(4);
consloe.log(arr,newArr)   //1,2,3   1,2,3,4
#### //3.2解构赋值针对*多维数组*属于浅拷贝
let arr2 = [[1,2,3],[4,5,6]];
let newArr2 = [...arr2];
newArr2[0].push(666);
console.log(arr2,arrArr2)   //[1,2,3,666],[4,5,6]     [1,2,3,666],[4,5,6]
#### //3.3深拷贝用法
let list =[
{id:1,stuName:'小明'class:'无年级二班'},
{id:1,stuName:'小红'class:'无年级三班'},
{id:1,stuName:'小绿'class:'无年级四班'},
]
let newList = JSON.parse(JSON.stringify(list)); //常见的深拷贝方式80%的场景    因为如果有方法会拷贝不出来   把function转化为'function'
newList.push({id:888})
console.log(list,newList)   //list不会被加上id:888
#### //3.4标准的深拷贝 => 专门针对引用数据类型(数组,对象)
   function deepClone (source) {
        //[]  =>Array(基类) { } => object
        const targetObj = source.constructor === Array ? [] : {};
        for (let keys in source) {
            if (source.hasOwnProperty(keys)) {
                //keys=> 3  有三中情况
                //引用数据类型
                if (source[keys] && typeof source[keys] === 'object') {
                    // 下面这行    运行时可以去掉      只是说明一下在内部判断时可能出现多种情况
                    targetObj[keys] = source[keys].constructor === Array ? [] : {};   //容错代码    提示代码(维护代码)
                    //递归
                    targetObj[keys] = deepClone(source[keys])
                } else {
                    targetObj[keys] = source[keys];
                }
            }
        }
        return targetObj
    }
    let objC = {
        ff: 'name',
        gg: 1,
        obj: { str: '1111', age: 12 },
        arr: [1, 2, 3, 4]
    }
    let newObjc = deepClone(objC);
    newObjc.ff = "有钱不买收音机";
    newObjc.arr.push("就听我吹牛逼");
    console.log(objC, newObjc)

7.输入URL的一瞬间浏览器都做了什么?

在这里插入图片描述
在这里插入图片描述

8.三次握手

第一次握手:客户端向服务端发起建立连接请求
握手前:客户端的状态为close,服务端的状态肯定也是close
握手后:客户端状态为SYN-SENT(同步发送),服务端状态为listen(倾听)
第二次握手:服务端收到客户端的报文会给客户端返回一段报文
握手前:客户端状态为SYN-SENT(同步发送),服务端状态为 listen(倾听)
握手后:客户端状态为SYN-SENT(同步发送),服务端状态为SYN-RCVD
第三次握手:客户端收到服务端的报文,再向服务端发送报文
握手前:客户端状态为SYN-SENT(同步发送),服务端状态为SYN-RCVD
握手后:客户端和服务端状态都为estadlished(建立连接)

握手的目的:客户端与服务端互相确定彼此是否有接收,发送的能力。

9.闭包

1.什么是闭包

闭包是指有权访问另一个函数作用域中的变量的函数。
在这里插入图片描述

//示例
  // 闭包的应用   处理私有化数据
    let makeCounter = function () {
        let privateCounter = 0;
        function changeBy (val) {
            privateCounter += val;
        }
        return {
            increment: function () {
                changeBy(1);
            },
            decrement: function () {
                changeBy(-1);
            },
            value: function () {
                return privateCounter;
            }
        }
    };
    let fun1 = makeCounter();
    fun1.increment()
    console.log(fun1.value());
    -----------------------------------------------------------------------------------------------------------
//思考题   点击li打印li对应的下标
<body>
    <ul>
        <li class="box">i</li>
        <li class="box">i</li>
        <li class="box">i</li>
        <li class="box">i</li>
    </ul>
</body>
<script>

    // var elements = document.getElementsByTagName('li');
    // for (var i = 0; i < elements.length; i++) {
    //     elements[i].onclick = function () {
    //         console.log(i);   //4
    //         alert(i);         //4
    //     };
    // }
    //function()的作用域中没有变量i,所以在父级作用域寻找,父级中有i,且for循环执行完毕,i已被赋值为4


    var elements = document.getElementsByTagName('li');
    for (let i = 0; i < elements.length; i++) {
        elements[i].onclick = function () {
            return (function (n) {
                console.log(n);
                alert(n);
            })(i);
        };
    }
</script>

2.为什么使用闭包

避免变量被污染
私有化
保存变量,常驻内存

3.闭包的使用场景

防抖,节流

10.事件委托(事件代理)

把原本需要自己干的事情委托给父亲或者爷爷或者祖先

原生的js事件委托(点击标签打印标签的内容)

<body>
    <ul id="ul">
        <li>0</li>
        <li>1</li>
        <li>2</li>
        <span>span0.5span</span>
        <li>3</li>
        <li>4</li>
    </ul>
</body>
<script>
    let ul = document.getElementById("ul")
    ul.onclick = function (event) {
        console.log(event);
        event = event || window.event
        let target = event.target;
        if (target.nodeName == 'LI') {
            alert("点击了内容为---------" + target.innerHTML + '----------li')
        }
    }
</script>

11.apply()–cell()–bind()

三者的区别:

1.apply和call不需要调用 bind需要调用
2.apply的参数都要放在数组中,call和bind对参数类型没有要求,用逗号分隔就行

用法:

    let obj = {
        name: '小明',
    }
    let testObj = {
        name: '张三',
        age: 18,
        sex: '男',
        saiHi: function (...argument) {
            console.log('我是' + this.name, argument);
        }
    }
    // apply()
    testObj.saiHi(); // 我是张三 []
    // 参数1(必填) 要修改的this指向的对象
    testObj.saiHi.apply(obj); // 我是小明 []
    // 参数二(可选)数组或伪数组,将其作为参数给调用的函数
    testObj.saiHi.apply(obj, [1, 2, 3]); // 我是小明 [1, 2, 3]
    testObj.saiHi.apply(obj, { getName: '王五' }); // 我是小明  []
    testObj.saiHi.apply(obj, [getName = '王五']); // 我是小明  ['王五']
    testObj.saiHi.apply(obj, { getName: '王五', length: 2 }); // 我是小明  [undefined, undefined]


    // //call()
    // // 参数1(必填) 要修改的this指向的对象
    // testObj.saiHi.call(obj); // 我是小明 []
    // // 参数二(可选)可以传任意值
    // testObj.saiHi.call(obj, [1, 2, 3]); // 我是小明 [1, 2, 3]
    // testObj.saiHi.call(obj, { getName: '王五' }); // 我是小明  {getName: '王五'}
    // testObj.saiHi.call(obj, { getName: '王五', length: 2 }); // 我是小明  {getName: '王五', length: 2}
    // // 参数N(可选)可以传多个值,同参数二
    // testObj.saiHi.call(obj, { getName: '王五', length: 2 }, true, 'hello'); // 我是小明  [{getName: '王五', length: 2}, true, 'hello']


    // //bind()
    // // 参数1(必填) 要修改的this指向的对象
    // testObj.saiHi.bind(obj)(); // 我是小明 []
    // // 参数二(可选)可以传任意值
    // testObj.saiHi.bind(obj, [1, 2, 3])(); // 我是小明 [1, 2, 3]
    // testObj.saiHi.bind(obj, { getName: '王五' })(); // 我是小明  {getName: '王五'}
    // testObj.saiHi.bind(obj, { getName: '王五', length: 2 })(); // 我是小明  {getName: '王五', length: 2}
    // // 参数N(可选)可以传多个值,同参数二
    // testObj.saiHi.bind(obj, { getName: '王五', length: 2 }, true, 'hello')(); // 我是小明  [{getName: '王五', length: 2}, true, 'hello']

12.js是如何实现继承的?

1.原型链继承

让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就是具有该实例的属性。

 // 1.原型链继承
    // 定义一个方法parent
    function Parent () {
        this.isShow = true
        this.info = {
            name: "abc",
            age: 18,
        };
    }
    //在parent方法的原型上添加getInfo方法
    Parent.prototype.getInfo = function () {
        console.log(this.info);
        console.log(this.isShow);
    }


    // 定义一个child方法
    function Child () { };
    // **让Child的原型是Parent的实例,new出来的Child的实例就具有Parent实例的属性**    
    Child.prototype = new Parent();

    //**new出来的Child的实例就具有Parent实例的属性**
    let Child1 = new Child();
    Child1.info.gender = "男";
    Child1.getInfo() //{name:'abc',age:18,gender:'男'} true

    let Child2 = new Child();
    Child2.isShow = false
    Child2.info.gender = "女";
    Child2.getInfo() //{name:'abc',age:18,gender:'女'} false

优点:写法方便简洁,容易理解
缺点:对象实例共享所有继承的属性和方法。无法向父类构造函数传参。

2.借用构造函数继承

在子类型构造函数的内部调用父类型构造函数:使用apply()或者call()方法将父对象的构造函数绑定在子对象上。

 // 定义一个方法parent
    function Parent (gender) {
        this.info = {
            name: "yyy",
            age: 18,
            gender: gender
        };
    }

    function Child (gender) {
        Parent.call(this, gender)
    }

    let child1 = new Child('男');
    child1.info.nickname = 'xxxx'
    console.log(child1.info);   //{name:'yyy',age:18,gender:'男',nickname:'xxxx'}

    let child2 = new Child('女');
    console.log(child2.info);   //{name:'yyy',age:18,gender:'女'}

优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法复用。 在父类的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。

3.组合继承

将原型链 和 借用构造函数 组合在一起。

    // 定义一个方法parent
    function Parent (gender) {
        console.log('121');
        this.info = {
            name: "yyy",
            age: 18,
            gender: gender
        };
    }

    Parent.prototype.getInfo = function () {
        console.log(this.info.name, this.info.age);   //使原型链继承原型上的属性和方法
    }
    function Child (gender) {
        Parent.call(this, gender)   //使用构造方法传递参数
    }

    let child1 = new Child('男');
    child1.info.nickname = 'xxxx'
    console.log(child1.info);   //{name:'yyy',age:18,gender:'男',nickname:'xxxx'}

    let child2 = new Child('女');
    console.log(child2.info);   //{name:'yyy',age:18,gender:'女'}
    
    

优点:解决了原型链继承和构造函数继承的缺点。
缺点:每次都会调用两次父类构造函数(创建子类原型和子类构造函数内部)

4.ES6,class实现继承

class通过extends关键字实现继承,其实是先创建出父类的this对象,然后用子类的构造函数修改this
子类的构造方法中必须调用super方法,且只有在调用了super()之后才能使用this,因为子类的this对象是继承父类的this对象,然后对其进行加工,而super方法表示的是父类的构造函数,用来新建父类的this对象。

 // 继承关键字 =>extends

    // 定义一个 人类
    class Person {
        // 类似于之前的构造函数
        constructor(name, age) {
            console.log("触发Person的构造函数");
            this.name = name
            this.age = age
        }
        // 底层这两个方法,就是添加到Person.prototype上
        sayHi () {
            console.log("你好@吖");
        }
        jump () {
            console.log("会跳");
        }
    }
    const p = new Person('张三', 18)
    console.log(p);


    // 定义一个 老师类  继承于  人类
    class Teacher extends Person {
        // 如果没有定义构造函数,在继承的时候会默认自动借调父构造函数
        constructor(name, age, lesson) {
            // 现在写的构造函数中,没有借调父构造函数
            super(name, age)   //触发调用父构造函数,进行实例的属性初始化
            this.lesson = lesson
        }
        teach () {
            console.log("会教书");
        }
    }
    const teacher = new Teacher('zs', 18, '教体育')
    teacher.jump()
    teacher.sayHi()
    teacher.teach()
    console.log(teacher);



优点:语法简单易懂,操作方便。
缺点:不是所有的浏览器都支持class关键字

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值