先简单介绍简历上的项目。
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存在双向绑定失效的情况,
- 当用索引直接设置一个数组项时,例如:array[index] = newValue
- 当修改数组的长度时,例如:array.length = newLength
- 由于 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表示要设置的数组值或对象值。