总结网址:前端导航
GitHub - sindresorhus/awesome: 😎 Awesome lists about all kinds of interesting topics
1.js是面向对象还是基于对象?
面向对象的三大特点:封装,继承,多态。
面向对象:先有一个抽象的类,然后根据这个类去实例化对象。
eg:java实例化对象
public class A{
private String name ;
private int age;
public void A(String name,int age){
this.super();
this.name = name;
this.age = age
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name
}
}
//对象实例化
A objA = new A("DaMing",18);
eg:js实例化对象
//点击按钮,改变div的样式--面向对象(高级)
//ChangeStyle是构造函数
function ChangeStyle(btnObj,divObj,json){
this.btnObj=btnObj;
this.divObj=divObj;
this.json = json;
}
ChangeStyle.prototype.init = function() {
//点击按钮改变div样式
var that = this; // 此处必须转存this,因为在function中this表示该点击事件的对象
this.btnObj.onclick=function() {
for(var key in that.json){
that.divObj.style[key]=that.json[key];
}
}
}
//实例化对象
var btnObj=document.getElementById("btnId");
var divObj=document.getElementById("divId");
var json = {"width": "500px","height": "500px","backgroundColor": "green","opacity": "0.1",}
var test = new ChangeStyle(btnObj, divObj, json);
test.init();//调用方法
基于对象:现有一个具体的类再去实例化对象。基于对象是没有继承的说法
2.JavaScript 中 ??
与 ||
的区别
??用法: 表达式1 ?? 表达式2
如果表达式1为undefin或null时返回表达式2
|| 用法 表达式1 || 表达式2
如果表达式1为false返回表达式2
??
更加适合在不知道变量是否有值时使用。
3.前端支持图片:
png-8
、png-24
、jpeg
、gif
、svg
- Webp:
WebP
格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG
的2/3
,并能节省大量的服务器带宽资源和数据空间。Facebook Ebay
等知名网站已经开始测试并使用WebP
格式。 - 在质量相同的情况下,WebP格式图像的体积要比JPEG格式图像小
40%
。 - Apng:全称是
“Animated Portable Network Graphics”
, 是PNG的位图动画扩展,可以实现png格式的动态图片效果。04年诞生,但一直得不到各大浏览器厂商的支持,直到日前得到iOS safari 8
的支持,有望代替GIF
成为下一代动态图标准
4.一个页面上有大量的图片(大型电商网站),加载很慢,你有哪些方法优化这些图片的加载,给用户更好的体验。
- 图片懒加载,在页面上的未可视区域可以添加一个滚动事件,判断图片位置与浏览器顶端的距离与页面的距离,如果前者小于后者,优先加载。
- 如果为幻灯片、相册等,可以使用图片预加载技术,将当前展示图片的前一张和后一张优先下载。
- 如果图片为css图片,可以使用
CSSsprite
,SVGsprite
,Iconfont
、Base64
等技术。 - 如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的缩略图,以提高用户体验。
- 如果图片展示区域小于图片的真实大小,则因在服务器端根据业务需要先行进行图片压缩,图片压缩后大小与展示一致。
- 图片裁剪
5.如何使用CSS实现硬件加速?
硬件加速是指通过创建独立的复合图层,让GPU来渲染这个图层,从而提高性能,
- 一般触发硬件加速的
CSS
属性有transform
、opacity
、filter
,为了避免2D动画在 开始和结束的时候的repaint
操作,一般使用tranform:translateZ(0)
6. 重绘和回流(重排)是什么,如何避免?
- 重绘:当渲染树中的元素外观(如:颜色)发生改变,不影响布局时,产生重绘
- 回流:当渲染树中的元素的布局(如:尺寸、位置、隐藏/状态状态)发生改变时,产生重绘回流
- 注意:JS获取Layout属性值(如:
offsetLeft
、scrollTop
、getComputedStyle
等)也会引起回流。因为浏览器需要通过回流计算最新值 - 回流必将引起重绘,而重绘不一定会引起回流
7.如何最小化重绘(repaint)和回流(reflow):
- 需要要对元素进行复杂的操作时,可以先隐藏(
display:"none"
),操作完成后再显示 - 需要创建多个
DOM
节点时,使用DocumentFragment
创建完后一次性的加入document
- 缓存
Layout
属性值,如:var left = elem.offsetLeft;
这样,多次使用left
只产生一次回流 - 尽量避免用
table
布局(table
元素一旦触发回流就会导致table里所有的其它元素回流) - 避免使用
css
表达式(expression
),因为每次调用都会重新计算值(包括加载页面) - 尽量使用
css
属性简写,如:用border
代替border-width
,border-style
,border-color
- 批量修改元素样式:
elem.className
和elem.style.cssText
代替elem.style.xxx
8.如何理解闭包?
1.定义:
官方定义:
是指拥有多个变量和绑定了这些变量的环境的 表达式(通常是一个函数),因而这些变量也是该表达式 的一部分。
通俗来说就是在函数内部定义函数,内部的函数可以拿到外部函数的变量。
2.用途:
用于读取函数内部变量,以及一直把变量保持在内存中
变量一直存在在内存中是因为内部函数引用了外部函数的变量,变量得不到释放,就会一直存在在内存中。这也会引起另一个问题就是内存泄漏,所以记得在变量不用时记得设为null。
9.事件句柄和事件源:
事件句柄:发生事件时要执行的操作。
事件源:产生事件的标签或元素。
10.js异步编程
- 回调函数
将函数作为参数传递到另一个函数中去执行
官方定义: A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
例子:
$("#dom").click(function(){
alert("事件回调")
});
优点:
简单、容易理解
解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着, 会拖延整个程序的执行。
缺点:
易形成回调地狱
不能用try...catch捕获错误
不能return
不利于维护,代码耦合高
- promise
Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。
promise是ES6支持的新语法在较低的浏览器版本是不支持的。promise有三个方法then(),
catch(),finally(),其中then方法可多次调用,finally执行与代码位置有关,catch和finally也可多次使用,但是catch无异常时只会调用一次,建议catch和finally只使用一次
用法:
var promise = new Promise(function(resolve,reject){
}).then(function(){
}).catch(function(){
}).finally(function(){
})
promise的两个方法:
Promise.all(arr);接受一个由promise构成的数组,异步执行后将返回结果合成一个数组返回一个promise.race(arr):接受一个由promise构成的数组,哪个promise执行的快以谁的数据执行回调resolve();
promise优点:
解决了回调地狱的问题,将异步操作以同步操作的流程表达出来。
Promise 的一个重要优点是它将逐渐被用作浏览器的异步 API ,统一现在各种各样的 API ,以及不兼容的模式和手法。
链式处理,写法更加清晰
加入了错误处理方法catch()
promise缺点:
不能中途取消promise,一旦新建就会立即执行
当promise未执行完好处于pending状态时,不能知道处于什么状态
手写promise:简单版
var PENDING = 'pending';
var FULFILED = "fulfilled";
var REJECTED = "rejected";
function MyPromise(fn){
this.status = PENDING;//初始状态
this.value= null;//初始化resolved参数
this.reason = null;//初始化rejected参数
// 构造函数里面添加两个数组存储成功和失败的回调
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
var that = this;
var resolved = function(value){
if(that.status==PENDING){
that.value = value;
that.status=FULFILED;
that.onFulfilledCallbacks.forEach(callback=>{
if(typeof callback == 'function'){
callback(value)
}
})
}
}
var rejected = function(reason){
if(that.status==PENDING){
that.value = value;
that.status=REJECTED;
that.onRejectedCallbacks.forEach(callback=>{
if(typeof callback == 'function'){
callback(reason);
}
})
}
}
try {
fn(resolved, rejected);
} catch (error) {
rejected(error);
}
}
MyPromise.prototype.then = function(resolve,reject){
if(this.status == PENDING){
this.onFulfilledCallbacks.push(resolve);
this.onRejectedCallbacks.push(reject);
}else if(this.status == FULFILED){
resolve(this.value);
}else{
reject(this.reason);
}
return this;
}
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1000);
}, 2000);
});
p.then((res) => {
console.log('结果:', res); // 结果:1000
}).then(() => {
console.log('执行完成');
})
- async/await
async是用来修饰函数的,放在函数前就表示这是一个异步函数,会返回一个promise对象
async function fn(){
return 1;
}
await 关键字只能放到async 函数里面
面试题1:
// 说出下面代码的输出
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(() => {
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
*
* /
面试题变体:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');//微任务1 2
}
async function async2() {
await async3();
console.log('async2');//微任务1 1
}
async function async3() {
await async4();
console.log('async3');//微任务1
}
async function async4() {
console.log('async4');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');//宏任务1
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');//微任务2
});
console.log('script end');
/**
* script start
async1 start
async4
promise1
script end
async3
promise2
async2
async1 end
setTimeout
* ***/
改写async/await为promise
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
new Promise(function(resolve,reject){
console.log('async1 start');
console.log('async2');
resolve();
}).then(function(){
console.log('async1 end');
})
- jquery的deferred对象
deferred对象是一个延迟对象,意思是函数延迟到某个点才开始执行,改变执行状态的方法有两个(成功:resolve和失败:reject),分别对应两种执行回调(成功回调函数:done和失败回调函数fail)
eg:
var wait=function(){
var tasks=function(){
console.log("执行完毕!");
};
setTimeout(tasks,5000);
};
$.when(wait())
.done(function(){console.log("success");})
.fail(function(){console.log("error")});
结果:
success
执行完毕!
var dtd=$.Deferred();
var wait=function(dtd){
var tasks=function(){
console.log("执行完毕!");
dtd.resolve();
};
setTimeout(tasks,5000);
return dtd;
};
$.when(wait(dtd))
.done(function(){console.log("success");})
.fail(function(){console.log("error")});
结果:
执行完毕!
success
11.es6的class
语法:
let MyClass = class{
constructor(a){
this.a = a;
}
}
let MyClass = class MyClass{
constructor(a){
this.a = a;
}
}
es5与es6写法对比
function MyClass(x,y){
this.x = x;
this.y = y;
console.log(this.x,this.y)
}
MyClass.prototype.name="MyClass";
MyClass.prototype.add = function(a,b){
console.log("a+b=",a+b);
return a+b;
}
var myclass = new MyClass(1,2);
myclass.add(5,5);
console.log(MyClass);
console.log(myclass);
console.log("=========================这是一条分割线=======================================")
class MyClass1{
constructor(x,y){
this.x = x;
this.y = y;
console.log(x,y)
}
add(a,b){
console.log("a+b=",a+b);
return a+b;
}
}
var myclass1 = new MyClass1(1,2);
myclass1.add(5,5);
console.log(MyClass1);
console.log(myclass1);
类上新增方法:
Object.assign(MyClass.prototype, { hello(){} });
类的定义不会被提升,类的方法也不需要function关键字,方法间不能加分号,
name属性
类名.name返回类名
constructor方法:默认方法,创建类的实例化对象被调用
this指向:指向该类的实例
静态方法:
class MyClass{
static add(x,y){
return (x+y);
}
}
原型方法:
class MyClass{
add(x,y){
return (x+y);
}
}
实例方法:
class MyClass{
constructor(){
add(x,y){
return (x+y);
}
}
}
类的实例: new MyClass();
类的getter和setter方法(这里取值和赋值需要前面添加下划线否则会不断递归报错RangeError)
class MyClass1{
constructor(x,y){
this.x = x;
this.y = y;
console.log(x,y)
}
get x(){
console.log("get");
return this._x;
}
set x(x){
console.log('set')
this._x = x;
}
add(a,b){
console.log("a+b=",a+b);
return a+b;
}
}
类的继承:(extends)
super:子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类
class MyClass1{
constructor(x,y){
this.x = x;
this.y = y;
console.log(x,y)
}
get x(){
console.log("get");
return this._x;
}
set x(x){
console.log('set')
this._x = x;
}
add(a,b){
console.log("a+b=",a+b);
return a+b;
}
static hello(){
return "hello";
}
}
class Son extends MyClass1{
constructor(x,y){
super(x,y)
}
minus(a,b){
console.log("a-b=",a-b)
return a-b;
}
static hello(){
console.log(super.hello()+this.name)
}
}
var myclass1 = new MyClass1(1,2);
var son = new Son(9,9);
console.log(son);
son.add(0,1);
son.minus(1,1)
Son.hello();
decorator:装饰器
简单来说就是对目标类进行修改操作,这个操作是在编译时发生的
装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false