你不可不知的JS面試題(第二期)
1、什么是繼承?
子類可以使用父類的所有功能,并且對功能進行擴展。
新增方法
改用方法
(1)、ES6使用extends子類繼承父類的方法。
// 父類
class A{
constructor(name){
this.name= name;
}
getName () {
return this.name;
}
};
// 子類繼承
class B extends A {
constructor(name){
super(name) // 記得用super調(diào)用父類的構(gòu)造方法!
}
getName(){
const name = super.getName();
return name;
}
}
var b = new B('2');
console.log(b.getName()); //2
(2)、ES5的繼承方法:
// 父類
function P(name) {
this.name = name;
}
// 父類方法
P.prototype.get=function(){
return this.name;
}
// 子類
function C(name){
P.call(this,name);
}
// 封裝繼承。也就是C.prototype.__proto__ = P.prototype
function I(Pfn,Cfn){
var prototype = Object.create(Pfn.prototype);
prototype.constructor = Cfn;
Cfn.prototype = prototype;
}
// 調(diào)用繼承方法,并傳入?yún)?shù)
I(P,C);
var c = new C('maomin');
console.log(c.get()); // maomin
(3)、ES3實現(xiàn)繼承
使用ES3實現(xiàn)繼承無非是替代了Object.create(Pfn.prototype),我們先來看下
大家知道我們封裝的I方法是原理是C.prototype.__proto__ =
P.prototype。但是我們不推薦這樣,因為__proto__是瀏覽器內(nèi)置的屬性,并不是JS內(nèi)置的,所以不推薦這樣做。我們來封裝一個方法來替代Object.create(Pfn.prototype)。
function objectCreate (o) {
function P1() {}
P1.prototype = o;
return new P1();
}
完整代碼:
// 父類
function P(name) {
this.name = name;
}
// 父類方法
P.prototype.get = function () {
return this.name;
}
// 子類
function C(name) {
P.call(this, name);
}
// 封裝object.create()
function objectCreate(o) {
function P1() {}
P1.prototype = o;
return new P1();
}
// 封裝繼承
//C.prototype.__proto__ = P.prototype;
function I(Pfn, Cfn) {
var prototype = objectCreate(Pfn.prototype);
prototype.constructor = Cfn;
Cfn.prototype = prototype;
}
// 調(diào)用繼承方法,并傳入?yún)?shù)
I(P, C);
var c = new C('maomin');
console.log(c.get()); // maomin
(4)、新增API
新增ES6方法 Reflect.setPrototypeOf()可以實現(xiàn)C.prototype.__proto__ = P.prototype
function A(name){this.name=name}
A.prototype.get=function () {return this.name}
function B (name) {A.call(this,name)}
Reflect.setPrototypeOf(B.prototype,A.prototype);
var b = new B('maomin');
console.log(b.get()); //maomin
2、關(guān)于Promise,你知道什么?
(1)、Promise是什么?
Promise是異步編程的一種解決方案,同時他有很多規(guī)范,如Promise/A,Promise/B,Promise/D以及Promise/A的升級版Promise/A+,而ES6中采用了Promise/A+規(guī)范。
(2)、Promise的作用是什么?
解決“回調(diào)地獄”問題
解決并發(fā)請求問題
解決異步編程代碼執(zhí)行順序理解困難的問題
① 解決“回調(diào)地獄”問題
我們先看下面代碼,看到會不會覺得太冗余了啊。如果代碼多的話,很難維護。
let count = 0;
setTimeout(() => {
count++;
console.log(`地獄${count}層`);
setTimeout(() => {
count++;
console.log(`地獄${count}層`);
setTimeout(() => {
count++;
console.log(`地獄${count}層`);
}, 500);
}, 500);
}, 500);
我們可以看到使用Promise讓它永遠在第一層,打印出 我還在人間 ,而不會越來越深。
let count = 0;
new Promise(resolve =>{
setTimeout(() => {
count++;
resolve();
}, 500);
}).then(()=>{
return new Promise(resolve=>{
setTimeout(() => {
count++;
resolve();
}, 500);
})
}).then(()=>{
console.log('我還在人間')
})
② 解決并發(fā)請求問題
可以在執(zhí)行result1 、result2 結(jié)束后再執(zhí)行下面的代碼
const result1 = fetch('/getName');
const result2 = fetch('/getAge');
Promise.all([result1,result2]).then(()=>{
// 執(zhí)行
})
③解決異步編程代碼執(zhí)行順序理解困難的問題
我們先看下這個場景,get方法是異步的方法,在執(zhí)行g(shù)etInfo方法時,并不會先執(zhí)行g(shù)et方法,而是先打印出我是getInfo方法。
function get() {
setTimeout(() => {
console.log('執(zhí)行g(shù)et方法');
}, 1000);
}
function getInfo() {
get();
console.log('我是getInfo方法');
}
getInfo();
// 我是getInfo方法
// 執(zhí)行g(shù)et方法
那么,我們使用Promise來解決異步,同時我們使用了ES6async與await來等待get方法執(zhí)行完再執(zhí)行下面的代碼。
function get() {
return new Promise((resolve)=>{
setTimeout(() => {
console.log('執(zhí)行g(shù)et方法');
resolve();
}, 1000);
})
}
async function getInfo() {
await get();
console.log('我是getInfo方法');
}
getInfo();
// 執(zhí)行g(shù)et方法
// 我是getInfo方法
3、如何實現(xiàn)Promise?
我們先來了解Promise
Promise包含then方法
then方法的兩個參數(shù)resolve和reject
Promise包含3個狀態(tài):pending(等待態(tài))、resolved(成功態(tài))、rejected(失敗態(tài))。
返回成功resolve:
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('success!') //返回成功狀態(tài)
}, 1000);
}).then((v)=>{
console.log(v);
},(e)=>{
console.log(e);
})
返回錯誤reject:
new Promise((resolve,reject)=>{
setTimeout(() => {
reject('error!') //返回失敗狀態(tài)
}, 1000);
}).then((v)=>{
console.log(v);
},(e)=>{
console.log(e);
})
好了,我們來實現(xiàn)一下,封裝一個Promise。
function myPromise(fn) {
this.status = 'pending'; // 初始化等待狀態(tài)
this.data = undefined; // 初始化一個存儲變量
this.resolvedCallback = []; //成功方法保存
this.rejectedCallback = []; // 失敗方法保存
const resolve = (val) => {
if (this.status === 'pending') {
this.status = 'resolved';
this.data = val;
this.resolvedCallback.forEach(fu => fu.call(this));
}
}
const reject = (val) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.data = val;
this.rejectedCallback.forEach(fu => fu.call(this));
}
}
fn(resolve, reject);
}
// 封裝then方法
myPromise.prototype.then = function (onResolved, onRejected) {
return new myPromise((resolve, reject) => {
const resolvedCallback = () => {
const result = onResolved(this.data);
if (result instanceof myPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
}
const rejectedCallback = () => {
const result = onRejected(this.data);
if (result instanceof myPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
}
if (this.status === 'resolved') {
resolvedCallback();
} else if (this.status === 'rejected') {
rejectedCallback();
} else { // this.status === 'pending'
this.resolvedCallback.push(resolvedCallback);
this.rejectedCallback.push(rejectedCallback);
}
})
}
// 使用
new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('success!');
}, 1000);
}).then((v) => {
console.log(v)
}, (e) => {
console.log(e)
}).then(() => {
console.log('1')
})
作者:Vam的金豆之路
主要領域:前端開發(fā)
我的微信:maomin9761
微信公眾號:前端歷劫之路