前端开发面经1

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

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

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

<div style="width: 100%; display: flex;">
    <div style="width: 200px; height: 200px;"></div>
    <div style="flex-grow: 1; height: 200px;"></div>
</div>

方法二: 定位布局下,使用calc()函数

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

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

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

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

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

console.log(Array.isArray(Array.prototype));    // true
// 附加各种数据类型的原型类型判断。Null和Undefined没有原型
console.log(Object.prototype.toString.call(Number.prototype));  // Number
console.log(Object.prototype.toString.call(String.prototype));  // String
console.log(Object.prototype.toString.call(Boolean.prototype)); // Boolean
console.log(Object.prototype.toString.call(Array.prototype));   // Array
console.log(Object.prototype.toString.call(Function.prototype));// Function
console.log(Object.prototype.toString.call(Object.prototype));  // Object
console.log(Object.prototype.toString.call(Date.prototype));    // Object
console.log(Object.prototype.toString.call(RegExp.prototype));  // Object
// 附加各种数据类型实例的隐式原型判断
let num = 123;
console.log(Object.prototype.toString.call(num.__proto__)); // Number
let str = '123';
console.log(Object.prototype.toString.call(str.__proto__)); // String
let bln = false;
console.log(Object.prototype.toString.call(bln.__proto__)); // Boolean
let arr = [1,2,3];
console.log(Object.prototype.toString.call(arr.__proto__)); // Array
let fct = function(){};
console.log(Object.prototype.toString.call(fct.__proto__)); // Function
let obj = {1:1,2:2,3:3};
console.log(Object.prototype.toString.call(obj.__proto__)); // Object
let dat = new Date();
console.log(Object.prototype.toString.call(dat.__proto__)); // Object
let reg = new RegExp();
console.log(Object.prototype.toString.call(reg.__proto__)); // Object

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

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

console.log(typeof Array.__proto__);    // Function
// 附加各种数据类型的原型属性类型判断
console.log(Object.prototype.toString.call(Number.__proto__));  // Function
console.log(Object.prototype.toString.call(String.__proto__));  // Function
console.log(Object.prototype.toString.call(Boolean.__proto__)); // Function
console.log(Object.prototype.toString.call(Array.__proto__));   // Function
console.log(Object.prototype.toString.call(Function.__proto__));// Function
console.log(Object.prototype.toString.call(Object.__proto__));  // Function
console.log(Object.prototype.toString.call(Date.__proto__));    // Function
console.log(Object.prototype.toString.call(RegExp.__proto__));  // Function

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

// 构造函数
function Father(){
  this.name = "father";
}
console.log(Object.prototype.toString.call(Father.prototype));  // Object
console.log(Object.prototype.toString.call(Father.__proto__));  // Function
// 实例
let tmp = new Father();
console.log(Object.prototype.toString.call(tmp));               // Object
console.log(Object.prototype.toString.call(tmp.__proto__));     // Object

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

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

(1)遍历

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

  打印shallow_copy的结果:

 

(2)用object.assign()

let origin = {
  a: 1,
  b: [2,3,4],
  c: {d: 'name'}
};

let shallow_copy = Object.assign({}, origin);
// 或者
// let shallow_copy = null;
// Object.assign(shallow_copy, origin);
console.log(shallow_copy);

打印shallow_copy的结果:

 

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

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

 shallow_copy的值会变。

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

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

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

 

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

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

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

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

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

事件委托针对的场景

用addEventListener()给所有<li>元素绑定事件,代码如下:

let children = document.querySelectorAll('li');
for(let i=0; i<children.length; i++){
  children[i].addEventListener('click', function(){
    console.log(this.id, this.innerHTML);
  })
}

<ul>
  <li id="0">你</li>
  <li id="1">好</li>
  <li id="2">啊</li>
</ul>

 打印结果如下:

 

但如果<li>元素有1000个,就会有1000个函数,非常影响浏览器性能。

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

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

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

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

子组件:

<template>
    <div>子组件中的input: </div><br/>
    <input type="text" :value="message" @input="changeData"><br/>
    <div>子组件中的message: {{message}}</div><br/>
</template>

<script>
export default{
    props: ["message"], // 父组件传来的数据
    emits: ['emit'],    // 父组件传来的事件
    methods:{
        changeData(e){
            // 触发父组件的emit事件
            this.$emit('emit', e.currentTarget.value);    
        }
    }
}
</script>

父组件:

<template>
  <!-- 用父组件的数据,绑到属性上,从而传给子组件 -->
  <Child :message="fatherData" @emit="childMethod"></Child>
  <div>父组件中的message: {{fatherData}}</div>
</template>

<script>
import Child from './components/Child.vue'
export default{
  data(){
    return{
      fatherData: "请输入"    // 要传给给子组件的数据
    }
  },
  methods:{
    childMethod(childData){    // 子组件要触发的函数
      this.fatherData = childData;
    }
  },
  components:{
    Child
  }
}
</script>

效果:

 修改输入值:

 可以看到,修改子组件的input值,父组件中message被$emit触发事件改变值,同时,父组件通过props传给子组件的message也随之改变。

6、vue数组变异方法。

Vue存在双向绑定失效的情况,

  1. 当用索引直接设置一个数组项时,例如:array[index] = newValue
  2. 当修改数组的长度时,例如:array.length = newLength
  3. 由于 JavaScript 的限制,Vue不能检测对象属性的添加或删除

对于数组,解决的方法有以下几种,

(1)数组原有方法

push()、pop()、shift()、unshift()、sort()、reverse()、splice()

(2)替换数组

不会改变原始数组,但总是返回一个新数组。

filter()、concat()、slice()

(3)修改响应数据

Vue.set(array, index, newValue)   // 修改数组某个下标的值
vm.$set(array, index, newValue)   // 同上
vm.array.splice(newLength)        // 修改长度

vm代表Vue的实例。参数array表示要处理的数组名称或者对象,index表示要处理的数组的索引或者对象名,newValue表示要设置的数组值或对象值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值