相信看到題目都知道,這些都是js千年不變的面試題。javascript
原型:至關於一個模具,用來生產實例對象。java
原型鏈:原型對象有個指針指向構造函數,實例對象又有一個指針指向原型對象,就造成了一條原型鏈,最終指向null。面試
原型:就是js裏實現面向對象的方式,也就是說,js就是基於原型的面向對象。閉包
原型鏈:是js實現繼承的方式。函數
所謂做用域,就是變量或者是函數能做用的範圍。ui
那麼JavaScript裏有什麼做用域呢?this
一、全局做用域spa
除了函數中定義的變量以外,都是全局做用域。prototype
舉個栗子:指針
var a = 10;
function bar(){
console.log(a);
}
bar();//10
複製代碼
以上的a就是全局變量,處處能夠訪問a。 然鵝,
var a = 10;
function bar(){
console.log(a);
var a = 20;
}
bar();//undefined
複製代碼
什麼鬼?undefined?
是的,你沒看錯。由於先搜索函數的變量看是否存在a,存在,又因爲a被預解析(變量提高),提高的a綁定了這裏的a做用域,因此結果就是undefined。
二、局部做用域
函數裏用var聲明的變量。
舉個栗子:
var a = 10;
function bar(){
var a = 20;
console.log(a);
}
bar();//20
複製代碼
三、沒有塊級做用域(至ES5),ES6中有塊級做用域
ES6以前,除了函數以外的塊都不具有塊級做用域。
常見的經典例子:
for(var i=0;i<4;i++){
setTimeout(function(){
console.log(i);
},200);
}
//4 4 4 4
複製代碼
解決辦法:
for(var i=0;i<4;i++){
(function(j){
setTimeout(function(){
console.log(j);
},200);
})(i)
}
//0 1 2 3
複製代碼
變量隨着做用長輩函數一級一級往上搜索,直到找到爲止,找不到就報錯,這個過程就是做用域鏈起的做用。
var num = 30;
function f1(){
var num = 20;
function f2(){
var num = 10;
function f3(){
var num = 5;
console.log(num);
}
f3();
}
f2();
}
f1();
複製代碼
閉包:js裏爲了實現數據和方法私有化的方式。內層函數能夠調用外層函數的變量和方法。
若是有這樣的需求
var go = function (a) {
var str = 'go';
var add0 = function (a) {
str += 'o';
return a ? str += a : add0;// 巧妙使用
}
return a ? str += a : add0;// 巧妙使用
}
console.log(go('l'));//gol
console.log(go()('l'));//gool
console.log(go()()('l'));//goool
複製代碼
既然前面說到繼承的問題。繼承指的是一個對象能夠共享父級對象的一些屬性。那麼爲何須要繼承?好比上文的問題中,形狀Shape有頂點這個屬性,三角形和矩形均可以繼承該屬性而不須要再從新定義。那麼就ES6之前跟ES6之後JavaScript中實現繼承的問題來聊聊吧。
組合繼承
function Parent(name, age) {
this.name = name;
this.age = age;
}
Parent.prototype.getName = function() {
return this.name;
}
function child(name, age, sex) {
Parent.call(this, name, age);
this.sex = sex;
}
child.prototype = new Parent()
var c1 = new child('zenquan', '23', 'M')
console.log(c1.getName())
console.log(c1)
複製代碼
這種繼承方式優勢在於構造函數能夠傳參,不會與父類引用屬性共享,能夠複用父類的函數,可是也存在一個缺點就是在繼承父類函數的時候調用了父類構造函數,致使子類的原型上多了不須要的父類屬性,存在內存上的浪費。
寄生組合繼承
function parent(name, age) {
this.name = name;
this.age = age;
}
parent.prototype.getName = function() {
return this.name;
}
function child(name, age, sex) {
parent.call(this, name, age);
this.sex = sex;
}
child.prototype = Object.create(parent.prototype, {
constructor: {
value: child,
enumerable: true,
writable: true,
configurable: true
}
})
var c1 = new child('zenquan', 23, 'M');
console.log(c1.getName())
console.log(c1)
複製代碼
以上繼承實現的核心就是將父類的原型賦值給了子類,而且將構造函數設置爲子類,這樣既解決了無用的父類屬性問題,還能正確的找到子類的構造函數。
ES6以後class繼承
以上兩種繼承方式都是經過原型去解決的,在 ES6 中,咱們可使用
class
去實現繼承,而且實現起來很簡單
class parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
return this.name;
}
}
class child extends parent {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
}
var c1 = new child('zenquan', 23, 'M');
console.log(c1);
複製代碼