• web前端面试


    web前端面试题总结

    1 HTML

    1.1 常见元素

    块级元素

    • h1-h6 标题
    • table cation tbody thead tfoot tr 表格的元素
    • ul ol li dt dd dl
    • form
    • p
    • div

    行内元素

    • a
    • b em i 废弃标签
    • code
    • label
    • span
    • th td

    行内块级元素

    • img
    • input
    • textarea

    h5

    • header
    • footer
    • nav
    • article
    • aside
    • datalist
    • section
    • video
    • audio

    2 CSS

    2.1 @import 和 link的区别

    • @import 引入的样式会面页面加载完成后才会加载,而link会和页面同时加载
    • link是标签,可以通过DOM去操作,而@import不可以

    2.2 实现一个左侧固定宽度200px,右边自适应布局

    // html
    <div class="left"></div>
    <div class="right"></div>
    

    第一种方法:浮动

    .left {
        float: left;
         200px;
        height: 500px;
        background: darkcyan;
    }
    
    .right {
        height: 500px;
        background: deepskyblue;
    }
    

    第二种方法:绝对定位

    body {
        position: relative;
    }
    
    .left {
        position: absolute;
        left: 0;
        top: 0;
         200px;
        height: 500px;
        background: darkcyan;
    }
    
    .right {
        height: 500px;
        background: deepskyblue;
    }
    

    第三种方法:flex

    .parent {
        display: flex;
    }
    
    .left {
         200px;
        height: 500px;
        background: darkcyan;
    }
    
    .right {
        height: 500px;
        background: deepskyblue;
        flex-grow: 1;
    }
    

    2.3 less定义一个圆角函数

    .radius(@r:50%) {
        -webkit-border-radius: @r;
        -moz-border-radius: @r;
        border-radius: @r;
    }
    

    2.4 w3c标准盒模型占位宽度是如何计算的? 它与IE盒模型有什么不同?

    IE67的混杂模式,宽度是算上了border + padding

    width = border-left + padding-left + con_width + padding-right + border-right
    

    标准盒模型是分开来计算的

    2.5 box-sizing的属性值content-box 和 border-box的计算方式?

    content-box 是w3c标准盒模型,其中的margin border padding width都是分别计算的,其中的width属性是只针对content的宽度。而border-box是IE67的混杂模型的盒模型计算方式,其中的width属性是盒模型的border + padding + content

    2.6 常见移动端适配不同屏幕

    • REM布局
    • 流式布局
    • flex布局

    2.7 引入css的方式

    • 行内式
    • 内嵌式
    • 外链式,使用link标签
    • 使用@import引入css

    2.8 写出position的属性值

    • static
    • relative
    • absolute
    • fixed

    2.9 用box-shadow 写出单边上阴影效果(阴影半径与模糊半径不限)

    div {
        -webkit-box-shadow: inset 5px 0 3px deepskyblue;
        -moz-box-shadow: inset 5px 0 3px deepskyblue;
        box-shadow: inset 5px 0 3px deepskyblue;
    }
    

    box-shadow: inset h v blur spreed color
    内阴影 竖直 水平 模糊 半径 颜色

    2.10 常见清除浮动的方式

    • 给父级指定高度
    • 父级最后加空元素
    • 父级overflow: hidden
    • 父级也一起浮动

    2.11 css3新增特性

    • border-radius
    • text-shadow
    • box-shadow
    • linear-gradient
    • box-sizing
    • word-warp
    • column-count
    • display: box

    2.12 a标签伪类的顺序

    link -> visited -> hover -> active
    原因如下:link和visited是常态,hover和active是即时状态,即时状态想要覆盖常态就必须要放到后面,否则无效。
    同样,在常态里,visited是访问后的状态,想要覆盖未访问状态就要写到其后面;
    即时态里,active想要覆盖hover就要写到后面

    2.13 样式权重的排序

    • !import 1000
    • id 100
    • class 10
    • tag 1

    2.14 display有哪些属性值,有什么作用

    • none:元素不会显示,而且改元素现实的空间也不会保留
    • inline: 将元素显示为内联元素,元素前后没有换行符
    • block:将元素显示为块级元素,元素前后会带有换行符
    • inline-block:行内块级元素
    • list-item:此元素会作为列表显示
    • table:此元素会作为块级表格来显示,表格前后带有换行符
    • inherit:规定从父元素继承display属性的值
    • box:弹性盒模型,c3新增

    2.15 Flex

    2.15.1 作用在父元素上的属性

    • display: flex
      设置这个属性后就会成为flex怪异盒模型

    • flex-direction
      设置flex主轴的方向,属性值有:row/row-reverse/colum/colum-reverse

    • flex-wrap
      设置换行的方式,属性值有:nowrap/wrap/wrap-reverse

    • justify-content
      设置项目在主轴上的排列方式:属性值有

    1. flex-start
    2. flex-end
    3. center
    4. space-between 两端对齐,中间均分
    5. space-around 两侧有间隙,这个间隙是项目之间间隙的一半
    • align-items
      项目在交叉轴上的对齐方式:属性值有
    1. flex-start
    2. flex-end
    3. center
    4. baseline
    5. stretch 默认值,占满空间
    • align-content多根轴的对齐方式,如果只有一根轴就不生效

    2.15.2 作用在项目上的属性

    • order 数值越小越靠前,默认为0
    • flex-grow 放大比例,如何去分配默认空间,0不放大,1均分,如果按其他比例,可以单独给项目设置
    • flex-shrink 缩小比例,0不缩小,1等比例缩小,默认值是1

    复合写法是flex

    • align-self 单独设置对齐方式

    3 JavaScript

    3.1 AJAX

    相关知识点:

    • 跨域(80%以上会问到)
    • fetch(替代xhr对象的东西)
    • socket

    3.1.1 JSONP

    跨域是浏览器的行为
    跨域需要服务端的配合,如果不能修改服务端,可以自己后台转发一下请求,毕竟后台请求是没有跨域限制的

    跨域的方式有几种,面试官要是问跨域的方式的话,一般就是在问JSONP。和面试官讲请JSONP的原理也算一点加分项。

    相关知识点:

    • 浏览器的同源,协议、域名和端口号
    • script标签的src属性没有跨域的限制
    • 需要服务端配合,前端需要给后台传回调名称

    总而言之,就是使用script标签把数据请求回来。

    一般印象中,script标签就是外链一段js代码。现在写一个服务,当浏览器访问/js时,就send('alert("js")'),发送一段js代码。现在看是否会执行
    Alt text

    Alt text

    Alt text

    可以看到js代码正常执行了。这个就等于直接往全局作用域里直接写了一行alert('js')

    这个时候我们再返回一个JSON串回去
    Alt text
    可以看到,直接报错了。这个就好比,直接往全局作用域里直接赤裸裸的扔了一个对象,没有任务操作。

    假如我们在全局作用域里已经定义好了一个函数

    function handle(data) {
    	console.log(data);
    }
    

    如果用script标签请求回来的数据,能直接用这个函数处理多好。所以后台给我们处理的时候

    res.send('handle({name: "chang"})')
    

    这个返回的数据和上面一样,也相当于直接在全局作用域里写了个

    handle({name: "chang"});
    

    这个时候我们早已经定义好了处理函数,那么这个函数就可以直接处理数据了

    所以不管哪个工具封装的JSONP都是这个操作,也就是为什么要和后台定义回调函数的名称,因为后台不知道你在JS定义什么名称的函数来处理数据,一般这个回调函数名称是需要放在queryString中的。后台需要截取,再拼接

    以下就是简单的JSONP代码

    function xxxx(content) {
    	console.log(content);
    }
    let script = document.createElement('script');
    script.src = 'http://127.0.0.1:8000?cb=xxxx';
    document.body.appendChild(script);
    

    所以综上,jsonp只能是get请求

    3.1.2 fetch

    fetch是之前浏览器的xhr对象的替代方案,现在使用的话,有浏览器兼容问题,毕竟有些浏览器是不支持的,可以使用whatwg-fetch这个包来解决一下。

    和原生的xhr一样,都不会直接使用原生,而是使用封装好的库。

    和原生的xhr相比,优点:

    • 使用promise
    • 进而可以使用async/await,写的就像同步代码一样爽
    try {
      let response = await fetch(url);
      let data = await response.json();
      console.log(data);
    } catch(e) {
      console.log("error:", e);
    }
    

    使用的坑:

    • 默认请求是不带cookie的,需要设置
      • fetch(url, {credentials: 'same-origin'})同域下发送cookie
      • fetch(url, {credentials: 'include'})跨域下发送cookie
    • 服务返回的400和500错误,是不会触发reject的,只有网络错误导致请求不能完成时,fetch才会触发reject

    3.1.3 socket

    一种前后台通信的方案,后台可以主动给前台推送消息,而取代之前的短轮循和长轮循

    3.2 JS输出问题

    3.2.1 对JS异步的理解

    说出下面数字的输出顺序:

    console.time('setTimeout');
    console.time('setImmediate');
    
    setImmediate(() => {
    	console.log(1);
    	console.timeEnd('setImmediate');
    });
    setTimeout(() => {
    	console.log(2);
    	console.timeEnd('setTimeout');
    }, 0);
    
    process.nextTick(() => console.log(3));
    
    new Promise(function(resolve){
    	console.log(4);
    	resolve();
    	console.log(5);
    }).then(function(){
    	console.log(6);
    });
    
    (() => console.log(7))();
    

    答案:

    4 5 7 3 6 2 1
    

    解析:同步代码先顺序执行,然后是本轮循环的异步任务和次轮循环的异步任务。
    process.nextTickPromise就属于本轮循环的,而且process.nextTick最先执行
    其他两个属性次轮循环的异步任务,在setTimeout的延迟时间为4ms及以下时,setTimeout会先执行

    3.2.2 JS的逗号语句

    var k;
    for (var i = 0, j = 0; i < 7, j < 10; i++, j++) {
    	k = i + j;
    }
    console.log(k);
    
    var l;
    for (var i = 0, j = 0; i < 10, j < 7; i++, j++) {
    	l = i + j;
    }
    console.log(l);
    

    3.2.3 alert()

    alert的时候会自动调用toString()方法

    alert([1, 3]); // -> 1, 3
    alert({name: 'chang'}); // -> [object Object]
    

    3.2.4 异步

    for (var i = 0; i < 5; i++) {
        setTimeout(function () {
    		console.log(i);
    	}, 1000*i);
    }
    

    结果是连续输出5个5。setTimeout执行时,i已经是5了,全部输出5很正常,现在的问题是时间间隔是多少。正确的姿势是,间隔时间是不相同的。(实验得知)

    3.2.5 一些和预解释相关的东西

    var obj = {name: 'Joo', age: 30};
    ~function (n) {
    	console.log(n);
    	n.name = '中国';
    	n = {};
    	n.age = 5000;
    	console.log(n);
    }(obj);
    console.log(obj);// {name: 'Joo', age: 30}
    
    
    // 形参 形式上的一个代表值
    // 解耦
    function add(a, b) {
    	console.log(arguments.callee.caller);
    }
    
    // 实参 实际传入的参数
    function a() {
    	add(1, 4);
    }
    a();
    

    3.3 数组

    3.3.1 数组的方法

    方法 参数 返回值 是否影响原数组 发布版本
    push length y
    pop item y
    shift item y
    unshift length y
    splice
    slice
    concat
    sort
    toString
    join
    reverse
    indexOf/lastIndexOf
    forEach
    every
    some
    filter
    map
    reduce/reduceRight
    find/findIndex
    includes
    fill
    entries/keys
    Array.from
    Array.of

    3.3.2 多维数组转一维数组

    第一种方法:使用递归

    var arr = [1, 2, [3, 4, [5, 6], 7], 8, [9, 10, [11]], 12];
    var spreadArr = [];
    
    function spread(arr) {
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] instanceof Array) {
            	spread(arr[i]);
    		} else {
            	spreadArr.push(arr[i]);
    		}
        }
    }
    
    spread(arr);
    console.log(spreadArr);
    

    3.3.3 数组相关的算法

    • 冒泡排序
    • 快速排序
    • 插入排序
    • 去重
    function bubbleSort(arr) {
    	let isCompleted = true;
        for (let i = 0; i < arr.length - 1; i++) {
            for (let j = 0; j < arr.length - i - 1; j++) {
             	if (arr[j] > arr[j + 1]) {
             		[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
             		isCompleted = false;
    			}
            }
            if (isCompleted) {
    			console.log('提前完成');
    			return arr;
    		} else {
            	isCompleted = true;
    		}
        }
        isCompleted = null;
        return arr;
    }
    
    function insertSort(arr) {
    	let newArr = [arr[0]];
    	for (let i = 1; i < arr.length; i++) {
    	 	let cur = arr[i];
    		for (let j = 0; j < newArr.length;) {
    	 	 	if (cur > newArr[j]) {
    	 	 		j++;
    	 	 		if (j === newArr.length) {
    	 	 			newArr.push(cur);
    					break;
    				}
    			} else {
    				newArr.splice(j, 0, cur);
    				break;
    			}
    	 	}
    	}
    	return newArr;
    }
    
    function quickSort(arr) {
    	if (arr.length <= 1) {
    		return arr;
    	}
        let midIndex = Math.ceil(arr.length / 2);
        let midVal = arr.splice(midIndex, 1)[0];
    	let left = [], right = [];
        for (let i = 0; i < arr.length; i++) {
         	let cur = arr[i];
         	cur < midVal ? left.push(cur) : right.push(cur);
        }
    	return quickSort(left).concat(midVal, quickSort(right));
    }
    
    function unique2(arr) {
    	let obj = {};
    	for (let i = 0; i < arr.length; i++) {
    		let cur = arr[i];
    		if (obj[cur] === cur) {
    			arr.splice(i, 1);
    			i--;
    		} else {
    			obj[cur] = cur;
    		}
    	}
    	return arr;
    }
    

    3.4 继承

    3.4.1 原型式继承

    function A(name) {
    	this.name = name;
    	this.sex = 1;
    }
    
    A.prototype.say = function () {
    	console.log('hello world');
    };
    
    function B(age) {
    	this.age = age;
    }
    
    let a = new A();
    
    B.prototype = a;
    
    let b = new B(18);
    
    console.log(b);//{ age: 18 }
    console.log(b.name, b.sex);//undefined 1
    b.say();//hello world
    
    
    /*
     * 原型链继承:子类的原型指定父类的实例
     *
     * extend:继承了父类的私有和公有属性,在此处name和sex是私有属性,say是公有属性
     * - 私有属性是从父类的实例上得到的
     * - 公的属性是沿着作用域链b.__proto__ --> a (没有) --> a.__proto__ --> A.prototype (保存在此处)
     *
     * 原型链继承的问题:
     * - 继承父类的私有属性时,无法向父类型中传参,所以在输出b.name的时候是undefined
     * - 在继承引用数据类型的值时,就会有问题,请看下例
     * */
    
    function SuperType() {
        this.color = ['red', 'blue', 'yellow'];
    }
    
    function SubType() {
    
    }
    
    SubType.prototype = new SuperType();
    
    let instance1 = new SubType();
    let instance2 = new SubType();
    
    instance1.color.push('black');
    console.log(instance2.color);//[ 'red', 'blue', 'yellow', 'black' ]
    
    //修改了instance1的属性的时候instance2的属性也会跟着变化,因为这个属性都是来自于它们的原型
    
    

    3.4.2 call继承

    function SuperType(age) {
    	this.age = age;
    }
    
    SuperType.prototype.say = function () {
    	console.log('hello world');
    };
    
    function SubType(age) {
    	SuperType.call(this, age);
    }
    
    let sub = new SubType(18);
    
    console.log(sub);//SubType { age: 18 }
    console.log(sub.say);//undefined
    
    /*
     * call继承:让父类的构造函数在子类的构造函数里执行,并使用call改变里面的this
     *
     * extend:因为只是让父类的构造函数在子类里执行,所以只继承了私有属性。
     *
     * 优点:和原型链继承相比,解决了无法传参的问题,这个参数可以写在call的第二个参数位置
     *
     * 缺点:
     * - 因为只有私有属性继承,定义在父类原型上的方法对子类的实例都是不可见的
     * */
    

    3.4.3 组合继承

    function SuperType() {
    	this.name = 'chang';
    }
    
    SuperType.prototype.say = function () {
    	console.log('hello');
    };
    
    function SubType() {
    	SuperType.call(this);
    }
    
    SubType.prototype = new SuperType();
    
    let sub = new SubType();
    
    console.log(sub);//{ name: 'chang' }
    sub.say();//hello
    
    /*
     * 组合式继承:将原型继承和call继承组合使用
     *
     * extend:使用call继承私有属性,使用原型链继承继承公有属性
     *
     * 优点:解决了原型继承和call继承的问题
     *
     * 缺点:父类的构造函数调用了两次
     *
     * */
    
    

    3.4.4 原型式继承

    /*
     * 这种方法并没有使用严格意义上的构造函数,借助原型可以基于已有的对象创新新的对象,同时还不必创建自定义的类型
     * */
    
    function object(o) {
    	function F() {
    	}
    
    	F.prototype = o;
    	return new F();
    }
    
    let obj1 = {name: 'chang', friend: ['a', 'b', 'c']};
    
    let obj2 = object(obj1);
    
    console.log(obj2.__proto__);//{ name: 'chang', friend: [ 'a', 'b', 'c' ] }
    
    /*
     * 在es5中规范了这个继承的方法:Object.create(),这个方法接收两个参数,一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象
     * */
    
    let obj3 = Object.create(obj1);
    
    console.log(obj3.__proto__);//{ name: 'chang', friend: [ 'a', 'b', 'c' ] }
    
    //添加额外属性时,必须使用这种格式
    let obj4 = Object.create(obj1, {
    	like: {
    		value: 'code'
    	}
    });
    
    console.log(obj4.like);//code
    

    3.4.5 __proto__

    function SuperType() {
    	this.name = 'chang';
    }
    
    SuperType.prototype.say = function () {
    	console.log('ok');
    };
    
    function SubType() {
    
    }
    
    SubType.prototype.__proto__ = SuperType.prototype;
    
    let sub = new SubType();
    
    console.log(sub.name);//undefined
    sub.say();//ok
    
    /*
     * 不知道名字的继承:改变子类的原型的__proto__,指向父类的原型
     * 只继承公有
     * */
    
    

    3.4.6 setProtoOf

    /*
     * Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
     *
     *
     * */
    
    let obj1 = {name: 'chang'};
    let obj2 = {};
    Object.setPrototypeOf(obj2, obj1);
    console.log(obj2.__proto__ === obj1);
    

    3.5 其他

    3.5.1 两个页面的通讯方式

    • url
    • localStorage
    • cookie

    3.5.2 统计页面中类名的个数

    var os = document.getElementsByTagName('*');
    var cObj = {};
    var max = 0;
    function sta(cName) {
        var cNameArr = cName.split(/ +/);
        for (var i = 0; i < cNameArr.length; i++) {
        	var cur = cNameArr[i];
            if (cObj[cur]) {
    			cObj[cur]++;
    		} else {
    			cObj[cur] = 1;
    		}
        }
    }
    for (var i = 0; i < os.length; i++) {
        if (os[i].className) {
        	sta(os[i].className);
    	}
    }
    
    for (var attr in cObj) {
        if (cObj.hasOwnProperty(attr)) {
            if (cObj[attr] > max) {
            	max = cObj[attr];
    		}
        }
    }
    console.log(max);
    

    3.6 ES6 +

    • let/const
    • 解构赋值
    • 字符串
      • 模板字符串
      • includes/startsWith/endsWith
      • repeat
    • 函数
      • 箭头函数
      • 参数默认值
      • 剩余参数
      • 双冒号
    • 数组
      • 扩展运算符
      • entries/keys/values
      • Array.from将类数组转化成数组
      • Array.of将一级值转成数组
      • includes
    • 对象的扩展
      • 简洁表达形式
      • 属性表达式
      • 函数的name
      • Object.assign合并对象,浅拷贝
      • Object.keys()/Object.values()/Object.entries()
      • 扩展运算符
      • 解构
    • Symbol第6种基本数据类型的值 let s = Symbol() // 不需要new
    • Set 可用于数组去重
    var arr = [1, 1, 1, 1];
    var s = new Set(arr);
    var newArr = [...s];
    
    • MapObject相比,key值可以是任何值
    • proxy做目标对象的读写操作的拦截
    • Promise
    • async/await
    • Class/extends
    • Module

    4 代码管理工具

    4.1 GIT

    常用命令(结合自己常用)

    4.1.1 提交4部曲

    • git add
    • git commit
    • git pull
    • git push在多人协作开发的时候,如果不pull是绝对push不上去的

    4.1.2 查看提交记录

    • git log
      如果想查看简单点就是
    • git log --pretty=oneline
      想看是某个人的提交历史
    • git log --author=changzhn
      想查看某个文件的提交历史
    • git log -- filename
      想在芒芒提交中搜索自己印象关键字
    • git log --grep=xxx
      终于找到那个提交的记录了,想看看提交了啥子内容,提交的ID截取前几位就可以了
    • git show hash

    4.1.3 分支管理

    查看当前的分支

    • git branch
      新建个分支并且切换过去
    • git checkout -b develop
      切换分支
    • git checkout develop/release/master
      注意:切换分支的时候,在当前分支修改的内容也是带过去,这一点很恶心,所以切换分支前,先使用下面这个命令,来看当前分支的状态
    • git status
      如果有修改(红色)或者添加(绿色)的内容,就要使用提交4部曲的addcommit来产生版本号后,才能切换分支
      或者,使用下面这个命令,将这个分支上的所有修改放入一个异次元...
    • git stash
      再切换回来想恢复修改的时候可以使用
    • git stash list
      想看,往异次元中放入了几次,如果只想恢复最近的一次的话
    • git stash pop stash@{0}
      Alt text

    比如切到release分支上想合并develop分支

    • git merge develop
      在合并的时候,很不幸运的是冲突了
      这个时候就需要解决冲突,在当前分支上,使用提交4部曲,为这次解决冲突产生一个版本号

    4.1.4 版本管理

    找到版本号的时候想回退代码

    • git reset --HARD hash
      比如现在将代码推到过程,后来发现这真是一次垃圾的提交,在本地使用代码回退上一个版本的时候,想再push上去,这个时候发现是推不上去的。
    • git push origin master -f
      可以使用-f来暴力提交
  • 相关阅读:
    jenkins系列---【linux安装nodejs】
    jenkins系列---【jenkins+gitlab+springCloud实现CI和CD】
    jenkins系列---【jenkins创建账号、分配角色】
    idea系列---【启动的时候报:Error running FinanceApplication. Command line is too long. Shorten the command line via JAR manifest or via a classpath file and rerun】
    ubuntu apt-get install xxx时一直报错E: Unable to locate package xxxxxxx
    mysql binlog 使用
    python项目导出所需要的依赖库
    mysql 导入sql文件时编码报错
    mysqldumps 远程备份
    pymongo的基本使用
  • 原文地址:https://www.cnblogs.com/changzhenan/p/8540493.html
Copyright © 2020-2023  润新知