• 前端开发面经1


    先简单介绍简历上的项目。

    1、用两种方法实现一个布局,左边div固定px,右边div占满剩余且随窗口变化。

    方法一:flex布局下,使用flex属性和flex-grow属性

    1. "width: 100%; display: flex;">
    2. <div style="width: 200px; height: 200px;">div>
    3. <div style="flex-grow: 1; height: 200px;">div>
  • 方法二: 定位布局下,使用calc()函数

    1. <div style="width: 100%; position: relative;">
    2. <div style="width: 200px; height: 200px;">div>
    3. <div style="position: absolute; top: 0; left: 200px; width: calc(100% - 200px); height: 200px;">div>
    4. div>

    2、显式原型和隐式原型。

    显式原型:每个函数都有prototype属性,指向它的原型对象。

    隐式原型:每个实例对象都有__proto__属性,指向实例的构造函数的原型对象。

    (1)问了Array的显式原型,这里总结所有类型的显示原型。并且打印每种类型的实例。可以看到类型的显示原型和实例的隐式原型是一致的。

    1. console.log(Array.isArray(Array.prototype)); // true
    2. // 附加各种数据类型的原型类型判断。Null和Undefined没有原型
    3. console.log(Object.prototype.toString.call(Number.prototype)); // Number
    4. console.log(Object.prototype.toString.call(String.prototype)); // String
    5. console.log(Object.prototype.toString.call(Boolean.prototype)); // Boolean
    6. console.log(Object.prototype.toString.call(Array.prototype)); // Array
    7. console.log(Object.prototype.toString.call(Function.prototype));// Function
    8. console.log(Object.prototype.toString.call(Object.prototype)); // Object
    9. console.log(Object.prototype.toString.call(Date.prototype)); // Object
    10. console.log(Object.prototype.toString.call(RegExp.prototype)); // Object
    11. // 附加各种数据类型实例的隐式原型判断
    12. let num = 123;
    13. console.log(Object.prototype.toString.call(num.__proto__)); // Number
    14. let str = '123';
    15. console.log(Object.prototype.toString.call(str.__proto__)); // String
    16. let bln = false;
    17. console.log(Object.prototype.toString.call(bln.__proto__)); // Boolean
    18. let arr = [1,2,3];
    19. console.log(Object.prototype.toString.call(arr.__proto__)); // Array
    20. let fct = function(){};
    21. console.log(Object.prototype.toString.call(fct.__proto__)); // Function
    22. let obj = {1:1,2:2,3:3};
    23. console.log(Object.prototype.toString.call(obj.__proto__)); // Object
    24. let dat = new Date();
    25. console.log(Object.prototype.toString.call(dat.__proto__)); // Object
    26. let reg = new RegExp();
    27. console.log(Object.prototype.toString.call(reg.__proto__)); // Object

    总结,原始类型(Number、String、Boolean)和引用类型(Array、Function、Object)的显式原型就是本身的类型。这些类型的显式原型和其实例的隐式原型是一致的,因此类型也是一致的。 

    (2)问了Array的隐式原型 ,这里总结所有类型的显示原型。

    1. console.log(typeof Array.__proto__); // Function
    2. // 附加各种数据类型的原型属性类型判断
    3. console.log(Object.prototype.toString.call(Number.__proto__)); // Function
    4. console.log(Object.prototype.toString.call(String.__proto__)); // Function
    5. console.log(Object.prototype.toString.call(Boolean.__proto__)); // Function
    6. console.log(Object.prototype.toString.call(Array.__proto__)); // Function
    7. console.log(Object.prototype.toString.call(Function.__proto__));// Function
    8. console.log(Object.prototype.toString.call(Object.__proto__)); // Function
    9. console.log(Object.prototype.toString.call(Date.__proto__)); // Function
    10. console.log(Object.prototype.toString.call(RegExp.__proto__)); // Function

    如果是自定义的构造函数与其实例,情况如下:

    1. // 构造函数
    2. function Father(){
    3. this.name = "father";
    4. }
    5. console.log(Object.prototype.toString.call(Father.prototype)); // Object
    6. console.log(Object.prototype.toString.call(Father.__proto__)); // Function
    7. // 实例
    8. let tmp = new Father();
    9. console.log(Object.prototype.toString.call(tmp)); // Object
    10. console.log(Object.prototype.toString.call(tmp.__proto__)); // Object

    总结,构造函数的隐式原型__proto__都是Function类型。除数据类型外,其他构造函数的显式原型prototype是Object类型,其实例的隐式原型和其构造函数的显示原型是一致的Object类型。不管是实例还是实例的隐式原型,它们的类型是一致的,因为要顺着原型链去找类型。

    3、实现object和array的浅拷贝。

    (1)遍历

    1. let origin = {
    2. a: 1,
    3. b: [2,3,4],
    4. c: {
    5. d: 'name'
    6. }
    7. };
    8. let shallow_copy = {};
    9. for(const key in origin){
    10. if(origin.hasOwnProperty(key)){ // 用hasOwnProperty()可以过滤掉原型上的属性和对象
    11. shallow_copy[key] = origin[key];
    12. }
    13. }
    14. console.log(shallow_copy);

      打印shallow_copy的结果:

     

    (2)用object.assign()

    1. let origin = {
    2. a: 1,
    3. b: [2,3,4],
    4. c: {d: 'name'}
    5. };
    6. let shallow_copy = Object.assign({}, origin);
    7. // 或者
    8. // let shallow_copy = null;
    9. // Object.assign(shallow_copy, origin);
    10. console.log(shallow_copy);

    打印shallow_copy的结果:

     

     但是如果修改origin的数据,再打印shallow_copy,

    1. origin.a = 6;
    2. origin.b[0] = 6;
    3. origin.c.d = '6';

     shallow_copy的值会变。

    如果修改shallow_copy的数据,然后打印origin,

    1. shallow_copy.a = 6;
    2. shallow_copy.b[0] = 6;
    3. shallow_copy.c.d = '6';

    origin只会改变引用类型的数据。

     

    (3)数组的其他浅拷贝方法Array.prototype.concat()和Array.prototype.slice()

    1. let arr1 = [1,3,{user: 'aaa'}]
    2. let arr2 = arr1.concat();
    3. let arr3 = arr1.slice();

    总结,浅拷贝和深拷贝都是针对对象的。对于对象的数值类型数据,浅拷贝会复制值,对于引用类型数据,浅拷贝只复制内存地址。因此对浅拷贝出来的新对象,修改引用类型的数据,会影响到原始对象。

    4、js的事件代理和事件委托。

    百度说,事件代理和事件委托实际上说的是同一件事,只是站在不同的角度来说的。比如说元素A把事件处理委托给自己的父元素B去处理,那么A就是事件委托方,而B就是事件代理方,两者参与的实际上是同一件事。下面就只说事件委托。

    事件委托针对的场景

    用addEventListener()给所有

  • 元素绑定事件,代码如下:

    1. let children = document.querySelectorAll('li');
    2. for(let i=0; ilength; i++){
    3. children[i].addEventListener('click', function(){
    4. console.log(this.id, this.innerHTML);
    5. })
    6. }
      • <li id="0">li>
      • <li id="1">li>
      • <li id="2">li>

     打印结果如下:

     

    但如果

  • 元素有1000个,就会有1000个函数,非常影响浏览器性能。

    事件委托就是来解决这个问题的。事件流是,捕获阶段——目标——冒泡阶段。事件委托发生在冒泡阶段。实现它的方法是,在父元素上绑定一个事件,利用该事件对象(里面的target)来判断当前事件流正在进行的元素。如果元素和期望元素相同,则执行相应的代码。

    1. let parent = document.querySelector('ul');
    2. parent.addEventListener('click', function(e){
    3. let event = e || window.event; // 获取event对象
    4. let target = event.target; // 获取target对象
    5. if(target.nodeName.toLowerCase() == 'li'){
    6. console.log(target.id, target.innerText);
    7. }
    8. })

    5 vue 自定义组件实现v-model的功能。

    其实考察父子组件通信。自定义组件要实现v-model的功能,即实现数据双向绑定,该自定义组件可看作子组件,引用该组件的为父组件。当父组件修改信息时,通过props传递给子组件,子组件的数据修改时,通过$emit派发事件给父组件修改数据。

    子组件: