web前端面试整理


面试官:什么是构造函数
答:构造函数的本质是一个普通函数,他的特点是需要通过new关键字来调用,用来创建对象的实例。所有的引用类型,如[],{},function等都是由构造函数实例化而来。一般首字母大写。

解析:首字母大写只是约定俗成的规范。首字母小写的函数也可以用作构造函数。

面试官:什么是原型和原型链
答:原型模式是JS实现继承的一种方式。所有的函数都有一个prototype属性,通过new生成一个对象时,prototype会被实例化为对象的属性。所有的引用类型都有一个__proto__指向其构造函数的prototype。原型链的话,指的就是当访问一个引用类型时,如果本身没有这个属性或方法,就会通过__proto__属性在父级的原型中找,一级一级往上,直到最顶层为止。

解析:原型链最顶层Object的prototype的__proto__指向为null。

面试官:如何理解constructor属性
答:所有函数的原型对象都有一个constructor属性指向函数本身。

解析:实例化的对象可以通过[].proto.constructor获取到其构造函数。

面试官:描述new 操作符的执行过程
答:

创建一个空对象。
将这个空对象的__proto__指向构造函数的prototype。
将构造函数的this指向这个对象。
执行构造函数中的代码。
面试官:如何判断一个变量是数组类型
答: 使用instanceof关键字 或者constructor属性。

解析:instanceof的原理是判断操作符左边对象的原型链上是否有右边构造函数的prototype属性。

1.js面向对象编程

JavaScript 的核心是支持面向对象的,同时它也提供了强大灵活的OOP语言能力

面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式。它使用先前建立的范例,包括模块化,多态和封装几种技术。今天,许多流行的编程语言(如Java,JavaScript,C#,C++,Python,PHP,Ruby和Objective-C)都支持面向对象编程(OOP)。

  • 面向编程分为面向对象和面向过程,C都为面向过程,OC将C封装起来,支持了面向对象

相对于“一个程序只是一些函数的集合,或简单的计算机指令列表。"的传统软件设计观念而言,面向对象编程可以看做时使用一系列对象相互协作的软件设计。在OOP中,每个对象能够接收消息,处理数据和发送消息给其他对象。每个对象都可以被看作是一个拥有清晰角色或责任的独立小机器。

面向对象程序设计的目的是在编程中促进更好的灵活性和可维护性,在大型软件工程中广为流行。凭借其对模块化的重视,面向对象的代码开发更简单,更容易理解,相比非模块化编程方法,它能更直接地分析,编码和理解复杂的情况和过程。

术语

  1. Namespace命名空间,在对象当中,设置一些具体的属性进行存值
  2. class类:对象的属性和方法的抽象和模板
  3. Object对象:类的一个实例,有类之后可以创建多个对象
  4. Property属性:对象的特征,比如姓名,年龄等名词
  5. Method方法:对象的能力,比如吃,跑等动词,就是对象的方法
  6. Constructor构造函数:可以构造当前类的一些属性
  7. Inheritance继承:一个类可以继承另一个类的特征
  8. Encapsulation封装:一种把数据和相关的方法绑定在一起使用的形式。将对象的属性和方法封装给对象
  9. Abstraction抽象:结合复杂的继承,方法,属性的对象能够模拟现实的模型。父级为抽象类,定义了很多方法和属性,子类可以使用
  10. Polymorphism多态:多意为‘许多’,态意为‘形态’。不同类可以定义相同的方法或属性
2.原型链

当一个对象调用自身不存在的属性和方法时就会去自己的proto的前辈prototype对象上去找,如果没找到就会去prototype的前辈上去找 直到找到或者返回undefiend 这个查找的过程就是原型链
1.prototype
每个函数都有一个prototype属性,被称为显示原型
2._ proto _
每个实例对象都会有_ proto 属性,其被称为隐式原型
每一个实例对象的隐式原型
proto _属性指向自身构造函数的显式原型prototype
3.constructor
每个prototype原型都有一个constructor属性,指向它关联的构造函数。
4.原型链
获取对象属性时,如果对象本身没有这个属性,那就会去他的原型__proto__上去找,如果还查不到,就去找原型的原型,一直找到最顶层(Object.prototype)为止。Object.prototype对象也有__proto__属性值为null。

  • 原型链是实现继承的主要方法,基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法。
    要理解原型链就要明白原型对象、构造函数、实例,三者之间的关系。 我们先来梳理三者的关系:
    每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,实力也有一个指向原型对象的内部指针。
    当这个原型对象是另一个类型的实例,那么这个原型对象就有一个内部指针指向另一个原型,以此类推就构成了一条原型链。原型链的根就是Object.prototype。

参考链接

3.闭包原理

闭包: 一个嵌套函数,子函数使用了父函数的变量,通过return将子函数暴露在全局作用域,子函数就形成闭包。
通过闭包,父函数的局部变量没有被销毁,可通过闭包去调用 ,但同时,这个局部变量也不会被全局变量污染。

优点: 避免全局变量的污染,同时,局部变量没有被销毁,驻留在内存中,还可以被访问
缺点: 使用不当,会造成内存泄露

闭包函数原理:

内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

闭包的作用:

实现公有变量

  1. 一个闭包域,每当声明了一个函数,它就产生了一个闭包域(可以解释为每个函数都有自己的函数栈), 每个闭包域(Function 对象)都有一个function scope(不是属性) ,function scope内默认有个名为 Globe的全局引用(有了这个引用,就可以直接调用 Globe 的属性或方法)
  2. 凡是在闭包域内声明的变量或方法,外部无法直接访问
  3. 闭包域可以访问外部的变量或方法
for (var i = 1; i <= 5; i++) {	//定时器才刚开始同时计时,变量i已经变成了6,所以输出的全部是6
  setTimeout( function timer() {
      console.log(i);			//输出5个6
  }, 1000 );
}
for (var i = 1; i <= 5; i++) {	//想要实现输出不同的数字,就需要把每个定时器所访问的变量独立起来,这就用到了JavaScript的闭包
    (function(i){
        setTimeout( function timer() {
              console.log(i);	//一秒过后输出1 2 3 4 5
          },  1000 );
    })(i);
}
for (let i = 1; i <= 5; i++) {	//在ES6中提出了一个新的关键字let,就可以声明一个仅对当前“{}”内部有作用的变量。输出的结果是一样。
  setTimeout( function timer() {
      console.log(i);			//一秒过后输出1 2 3 4 5
  }, 1000 );
}
4.跨域

什么是跨域?
协议不同,域名不同,端口号不同。

跨域的缘由
主要是因为浏览器指定的同源策略,“协议+域名+端口”都相同,他是浏览器最基本最核心的安全机制,关闭它会容易受到XSS和CSRF的攻击。

跨域的后果
1.cookie,LocalStorage,indexDB无法获取。
2.DOM和js对象无法获得
3.Aiax请求不能发送

目前比较常用的跨域解决方案有3种:

Jsonp
最早的解决方案,利用script标签可以跨域的原理实现。

限制:
需要服务的支持
只能发起GET请求
nginx反向代理

思路是:利用nginx反向代理把跨域为不跨域,支持各种请求方式

缺点:需要在nginx进行额外配置,语义不清晰

CORS

规范化的跨域请求解决方案,安全可靠。

优势:

在服务端进行控制是否允许跨域,可自定义规则
支持各种请求方式
缺点:

会产生额外的请求
cors的跨域方案用的比较多

nginx配置解决iconfont跨域

浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件例外,此时可在nginx的静态资源服务器中加入以下配置。

5.localStorage 存满了怎么办

localStorage最大存储空间为5M

  1. a.meituan.com 和 b.meituan.com 这两个域能够共享同一个 localStorage 吗?
  2. 在 webview 中打开一个页面:i.meituan.com/home.html,点击一个按钮,调用 js 桥打开一个新的webviewi.meituan.com/list.html,这两个分属不同 webview 的页面能共享同一个localStorage 吗?
  3. 如果 localStorage 存满了,再往里存东西,或者要存的东西超过了剩余容量,会发生什么?

答案

  1. 同一个域名(document.domain)共享同一个 localStorage,a.meituan.com 和
    b.meituan.com 是两个域名,所以不能共享
  2. 能。相当于同一个浏览器的不同标签页。不同浏览器之间不能共享。
  3. 存不进去并报错(QuotaExceededError)
6.Vue里面this指向,jq获取类时this指向

1.在vue官方文档中写道

在Vue所有的生命周期钩子方法(如created,mounted, updated以及destroyed)里使用this,this指向调用它的Vue实例

箭头函数中没有自己的this,所以在vue中的箭头函数指向也是Vue实例

对于普通函数(包括匿名函数)this指的是其直接调用者,也就是说谁调用了我这个函数,this指的就是谁。而且在非严格模式下(‘use strict’),如果没有直接调用者,this指的就是window

2.this指向核心的一句 :哪个对象调用函数,函数里面的this指向哪个对象

在全局中调用this,this指向全局对象

使用new实例时,会开辟新的内存,此时this指向新的内存

jq中获取dom时,this指向的是dom本身
7.前后端分离

前端:整个页面显示以及页面的交互逻辑。用ajax和node作为交互。其中node作为中间层。
后端:提供api接口,利用redis保存session,与数据库交互。
流程:
客户端(浏览器)向node请求页面交互。
node向后端(这里用java)转发请求。java在发送请求到数据库。
java返回结果给node。node返回页面,提供数据。

8.数组去重方法及原理

一、ES6语法,new Set()方法去重
这个是目前最简单的去重方法,但是这个方法不支持对象方法

function unique(arr) {  //数组去重es6
    return Array.from(new Set(arr))
}

二、定义一个去重方法,直接调用。
这个方法可以处理简单数据也可处理复杂数据

function setarr(array){
	var newarr = []; 一个新的临时数组
	for(var i = 0; i < array.length; i++){
		if(newarr.indexOf(array[i]) == -1){
			newarr.push(array[i]);
		}
	}
	return newarr;
}

三、利用数组原型对象splice 方法加上双重for循环

function unique(arr) {
	var i,j,len = arr.length;
	for (i = 0; i < len; i++) {
		for (j = i + 1; j < len; j++) {
			if (arr[i] == arr[j]) {
				arr.splice(j, 1);
				len–;
				j–;
			}
		}
	}
	return arr;
}
  • 总结:不管什么方法,我们不难发现,其实原理都是差不多,都是循环加判断实现,以下是我常用的方法仅供参考
function equar(a, b) {    //判断两个数组是否相同
    if (a.length !== b.length) {
        return false
    } else {
        for (let i = 0; i < a.length; i++) {
            if (a[i] !== b[i]) {
                return false
            }
        }
        return true
    }
}
Array.prototype.indexOf = function(val) { //获取数组中指定元素的索引位置
    for (var i = 0; i < this.length; i++) {
        if (this[i] == val) return i;
    }
    return -1;
};
Array.prototype.removeItem = function(val) {    //删除数组指定元素
    var index = this.indexOf(val);
    if (index > -1) {
        this.splice(index, 1);
    }
};
9.vue与react的区别

简介

Vue是渐进式JavaScript框架,核心思想是尽可能的降低前端开发的门槛,是一个灵活易用的渐进式双向绑定的MVVM框架。
React主张是函数式编程的理念,核心思想是声明式渲染和组件化、单向数据流,React既不属于MVC也不属于MVVM架构。

共同点

1.都使用虚拟dom, 数据驱动视图,都使用Virtual DOM + Diff算法。
2.提供了响应式和组件化的视图组件。
3.把注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。(vue-router、vuex、react-router、redux等等)

不同点

核心思想不同
vue是灵活易用的渐进式框架,进行数据拦截/代理,它对侦测数据的变化更敏感、更精确
React推崇函数式编程(纯组件),数据不可变以及单向数据流
React的diff和Vue的diff算法不同
响应原理不同
React主要是通过setState()方法来更新状态,状态更新之后,组件也会重新渲染。
vue会遍历data数据对象,使用Object.definedProperty()将每个属性都转换为getter和setter,每个Vue组件实例都有一个对应的watcher实例,在组件初次渲染的时候会记录组件用到了那些数据,当数据发生改变的时候,会触发setter方法,并通知所有依赖这个数据的watcher实例调用update方法去触发组件的compile渲染方法,进行渲染数据。
组件写法差异
React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中,即 all in js; Vue 推荐的做法是 template 的单文件组件格式(简单易懂,从传统前端转过来易于理解),即 html,css,JS 写在同一个文件(vue也支持JSX写法)

应用场景不同
1.构建一个大型应用项目时:React的渲染系统可配置性更强,和React的测试工具结合起来使用,使代码的可测试性和可维护性更好。大型应用中透明度和可测试性至关重要。
同时适用于Web端和原生APP时:React Native是一个使用Javascript构建移动端原生应用程序(iOS,Android)的库。 它与React.js相同,只是不使用Web组件,而是使用原生组件。
2.构建数据简单中小型应用时:vue提供简单明了的书写模板、大量api、指令等等,可快速上手、开发项目
应用尽可能的小和快时:随着vue3.0的发布,vue的体积进一步缩小,远小于react的体积,也配合diff算法,采用proxy去实现双向绑定,渲染大幅度提升

10.Rem,em区别,移动端怎么做兼容
px,绝对长度单位,像素的计算是针对屏幕的,一个像素(1px)就是屏幕的一个点
em,相对长度单位 ,相对于父元素,一般浏览器默认(1em=16px);
rem,相对长度单位,相对于根元素,即HTML元素,常用于响应式布局
html {
  font-size: 100% // 相当于16px
}
div {
  width: 3rem  // 相当于3*16px = 48px
}

移动端适配使用lib-flexible会和px2rem-loader插件一起使用,目的是将css中的px转换为rem

lib-flexuble它会自动将html中的font-size设置为屏幕大小的十分之一,即假设说屏幕大小为750px,那么html的font-size为75px,即1rem=75px,那么我们要设置的元素的宽度如果为150px,就可以设置为2rem
11.深拷贝与浅拷贝
浅拷贝最多拷贝对象的一层,深拷贝可能是拷贝对象的多层
浅拷贝是指,对基本类型(字符串,数字,布尔值,null,Undefined)的值拷贝,以及对象类型的地址拷贝
var a = 1;
var b = a; //浅拷贝
b = 2;     //改变b的值,并不会影响到a,因为浅拷贝对基本类型而言就是值拷贝
console.log(a); //1 a还是1,和b没有关系
var p1 = {
    name: 'jack'
}
 
var p2 = p1;
p2.name = 'rose';
 
console.log(p1); //p1也不会改变
深拷贝是指,除了拷贝基本类型的值,还完全复刻了对象类型
var p1 = {
    name: 'jack'
}
 
var p2 = {
    name: p1.name
};
p2.name = 'rose'; //p1.name也会改变

浅拷贝使用copy.copy()函数

深拷贝使用copy.deepcopy()函数

不管是给对象进行深拷贝还是浅拷贝,只要拷贝成功就会开辟新的内存空间存储拷贝对象。

浅拷贝和深拷贝的区别:浅拷贝最多拷贝对象的一层,深拷贝可能拷贝对象的多层

12.实际开发中都做过哪些性能优化
1.加载优化(减少http请求数),预加载, 异步加载第三方资源
2.图片优化,减少体积,赖加载,避免img、iframe等标签的src属性为空:空src会重新加载当前页面,影响速度和效率
3.使用CDN,CDN具有更低的网络延迟和丢包率,能够分配负载,节省带宽提高网站的性能
4.开启Gzip(代码压缩),Gzip即数据压缩,前端生产环境中将js、css、图片等文件进行压缩,通过减少数据传输量减小传输时间,节省服务器网络带宽,提高前端性能
5.样式表和JS文件的优化,css头部link引用,js尾部引用,异步方式加载
6.减少不必要的Cookie,cookie在访问对应域名下的资源时都会通过HTTP请求发送到服务器,从而会影响加载速度
7.脚本优化,将脚本往后挪,减少对并发下载的影响
8.前端代码结构的优化,设置Viewport,减少DOM结点,优化高频事件使用防抖节流
9.SEO优化,标题字数不要太长,描述,关键词,网站代码尽量精简,利于爬虫搜索
13.防抖节流
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

经典比喻:电梯第一个人进来后,15秒后准时运送一次,这是节流。

电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖

都可以通过使用 setTimeout 实现,函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值