前端面试题
1,遍历
let arr = [1,2,3];
arr.name = 'a';
Array.prototype.sex = "ds";
console.log(arr);//[1,2,3,name:'a']
for(var i=0;i<arr.length;i++) {
console.log(arr[i]) //1,2,3
}
//可以遍历对象和数组
for(var key in arr) {
console.log(key)//0,1,2,name,sex (hasOwnproperty)
}
console.log(Object.keys(arr));//['0','1','2','name']
//只能数组
for(var key of arr) {
console.log(key)//1,2,3
}
//只能数组
arr.forEach(i => {
console.log(i) //1,2,3
})
2, 逻辑运算符
1 && 2 若 1 可转换为 true,则返回 2;否则,返回 1。
1 || 2 若 1 可转换为 true,则返回 1;否则,返回 2。
可以转为false:
null;NaN;0;空字符串("" or '' or ``);undefined
alert(1 && 2); //2
alert(0 && 2); //0
alert(0 && undefined); //0
alert(1 || 2); //1
alert(0 || 2); //2
alert(0 || undefined); //undefined
3,闭包
for(var i=10;i>0;i--) {
(function(k){
setTimeout(function() {
console.log(k); //10,9,8,7
}, 0)
})(i)
}
4,变量提升
(function() {
console.log(name);//报错且终止运行。 在初始化之前未能Cannot access 'name' before initialization
console.log(age);//如果在报错之前的话可以输出undefined
let name = 's';
var age = 14;
})()
5,数字属性名==变量属性名的时候,
- 后者覆盖前者
let a={},b='0',c=0;
a[b]='超逸';
a[c]='博客';
console.log(a[b]); //博客
2)symbol声明唯一值
let a = {},b = Symbol('0'),c = Symbol(0);
a[c] = '博客';
a[b] = '超逸';
console.log(a[c]);//博客
对象属性名还可以是symbol.png
6, 多层嵌套深拷贝
function isArray(val) {
return Object.prototype.toString.call(val) === ['Object Array'];
}
function isObject(val) {
return typeof val === 'Object' && typeof val !== null;
//注意: typeof null/[]/{} === 'Object';
}
function DeepClone(val) {
var obj = isArray(val) ? [] : {};
for (var key in val) {
if (isObject(val[key])) {
obj[key] = DeepClone(val[key]);//递归
} else {
obj[key] = val[key];
}
}
return obj;
}
7: 综合
function Foo(){
getName=function(){
console.log(1);
};
return this;
}
Foo.getName = function (){
console.log(2);
};
Foo.prototype.getName = function (){
console.log(3);
};
var getName = function(){
console.log(4);
};
function getName(){
console.log(5);
}
Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//4 先Foo.getName() 无参数new
new Foo().getName();//3 先new Foo()实例,然后找构造函数原型上的getName
new new Foo().getName();//3
8,浏览器是多线程的,而js是单线程的
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2(){
console.log('async2');
}
console.log('script start');
setTimeout(function(){
console.log('setTimeout');
},0)
async1();
new Promise(function (resolve){
console.log('promise1');
resolve();
}).then(function(){
console.log('promise2');
});
console.log('script end');
script start ----async1 start ---async2
---- promise1(同步执行立即执行) -- script end ---- async1 end ---promise2(微任务)----settimeout(宏任务)
9,多维数组拉平
arr = [1, 2, [3, 4, [5, 6, [7, 8]]]].join(',').split(',');
//["1", "2", "3", "4", "5", "6", "7", "8"]
arr = [1, 2, [3, 4, [5, 6, [7, 8]]]].flat(Infinity) ;
//es6方法 [1, 2, 3, 4, 5, 6, 7, 8]
10,父子组件执行顺序:
image.png
11,块级作用域 let const 定义的不是顶层对象
const a = {
b: function(){console.log(this)},
c: ()=>{console.log(this);}
};
a.b(); //a
a.c(); //window
console.log(window.a);//undefined
let a = 'ss'; 等价于
{
let a = 'ss'
}
12,js实现es6数组方法
filter:
Array.prototype.filter1 = function(fn) {
console.log(this);//谁调用就是谁 [1,2,3,4]
var arr = [];
// 遍历数组,回调函数为true 就返回
this.forEach(i => {
if (fn(i)) {
arr.push(i);
}
})
return arr;
}
console.log([1,2,3,4].filter1(item => {return item > 2;}));//[3,4]
every:
Array.prototype.every1 = function(fn) {
console.log(this);//谁调用就是谁 [1,2,3,4]
var arr = [];
// 遍历数组,回调函数为true 就返回
this.forEach(i => {
if (fn(i)) {
arr.push(i);
}
})
// 每一个都符合条件
return arr.length === this.length;
}
console.log([1,2,3,4].every1(item => {return item > 2;}));//false
some:
Array.prototype.some1 = function(fn) {
console.log(this);//谁调用就是谁 [1,2,3,4]
var arr = [];
// 遍历数组,回调函数为true 就返回
this.forEach(i => {
if (fn(i)) {
arr.push(i);
}
})
// 至少有一个
return arr.length > 0;
}
console.log([1,2,3,4].some1(item => {return item > 2;}));//true
map:
Array.prototype.map1 = function(fn) {
console.log(this);//谁调用就是谁 [1,2,3,4]
var arr = [];
// 遍历数组,回调函数返回结果放到新数组
this.forEach(i => {
arr.push(fn(i));
})
return arr;
}
console.log([1,2,3,4].map1(item => {return item * 2;}));//[2,4,6,8]
find:
Array.prototype.find1 = function(fn) {
console.log(this);//谁调用就是谁 [1,2,3,4]
// 遍历数组,回调函数为真返回第一个元素并跳出循环
for(var i=0; i<this.length;i++) {
if(fn(this[i])) {
return this[i];
}
}
}
console.log([{a: 1,b: 2},{a: 111,b:222}, {a: 111, b:2}].find1(item => {return item.a === 111;}))//{a: 111,b:222}
13,意外全局变量
function foo() {
let a = b = 0;
a++;
return a;
}
foo();
console.log(typeof a); //undefind
console.log(typeof b, b); //number 0
14,length修改数组
const a = [1,2,3];
a.length = 0;
console.log(a[0]); //undefind
15, 块级作用域
const a = [1,2,3];
a = [];
console.log(a[0]); 报错,let可以 a.push(1)可以
16, for空语句
let arr = [], i = 0;
for(i = 0; i < 4; i++);
{
arr.push(i)
}
console.log(arr);//[4]
17, 折行
function ss(item) {
return
[item];
}
console.log(ss(10));//undefind return;[item]
18,访问地址做的事情
image.png
URI解析-----DNS解析----TCP三次握手----发送HTTP请求----服务端处理和响应----TCP四次挥手,关闭链接通道---浏览器解析渲染
19,前端优化
一)浏览器渲染解析
1,标签语义化,2,避免多级嵌套,解析快些。
image.png
2,选择器层级问题 特别长不好
image.png
3,真正渲染到页面中的时候:一定发生在DOM-Tree和CSSOM树都已经完成,已经生成render tree了才回流和重绘,HTML和css都是阻碍页面渲染的东西
4,link是发送有个HTTP请求,每一个HTTP请求都是单独线程去处理,所以dom树有时候会优先CSS。style比link好。link放前边,preload 提前加载。
5,js默认加载阻塞。放在底部
6,减少页面的回流: 少操作元素(VUE,react,)
二)DNS解析
image.png
三)缓存
image.png
20,vue过滤器:第一个参数默认是原始值。
filters: {
myfilter (value, arg) {
let s = value.split('')
let n = 0
s.forEach((elem) => {
if (elem === arg) {
n++
}
})
return n
}
}
21,vue data和computed和watch区别
1.如果一个数据依赖于其他数据,那么把这个数据设计为computed的
2.如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化。
22,vue中key的作用。
为了高效的更新虚拟DOM。
23,为什么vue中data返回一个函数。
如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改;
两个实例应该有自己各自的域才对。
24,addEventListener
button.addEventListener("click",function(){
alert(this.id);
},false); //false 冒泡阶段触发stopPropergation ;true: 捕获阶段触发 preventDefault
25,栈(stack)和堆(heap)
image.png
26, es6继承
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class child extends Parent {
constructor(name, age, sex) {
super(name, age);
console.log(this);
this.sex = sex; // 必须先调用super,才能使用this
}
}
27,防抖和节流
防抖:
动作绑定事件,动作发生后一定时间后触发事件,在这段时间内,如果该动作又发生,则重新等待一定时间再触发事件。(如果动作不停,就不会执行事件)
Input验证身份证号
function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
if (timeout) {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
}
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
// fn(); this指向window
}, 2000);
};
};
function say() {
console.log(this);
console.log('say');
};
$('#ipt').bind('input', debounce(say));
箭头函数 apply: 改变this指向,指向的是input;
image.png
如果不是箭头函数,this指向window
image.png
解决办法: 函数外this赋值给that
return function () {
let that = this;
if (timeout) {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
}
timeout = setTimeout(function() { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(that, arguments);
节流:scroll事件 。。动作绑定事件,动作发生后一段时间后触发事件,在这段时间内,如果动作又发生,则无视该动作,直到事件执行完后,才能重新触发。(每隔一段时间就会执行)
function showTop () {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);//1秒才打印一次,否则会打印很多很多
}
function throttle(fn){
let canRun = true;
return function(){
if(!canRun){
return
}
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, 1000)
}
};
window.onscroll = throttle(showTop);
28,深拷贝
遍历、递归
function deepClone(obj) {
let result = Array.isArray(obj) ? {} : [];
for(var key in obj) {
if (obj.hasOwnproperty(key)) {
if (typeof(obj[key]) === 'Object' && obj[key] !== null) {// typeof(null) === Object
result[key] = deepClone(obj[key]); //递归,对象深层嵌套
} else {
result[key] = obj[key];
}
}
}
}
转字符串后转对象
function deepClone(arr){
return JSON.parse(JSON.stringify(arr))
}
29,