農曆2019即將過去,趁着年前幾天上班事情少,整理了一下javascript的基礎知識,在此給你們作下分享,喜歡的大佬們能夠給個小贊。本文在github也作了收錄。javascript
本人github: github.com/Michael-lzg前端
每一個構造函數(constructor)都有一個原型對象(prototype), 原型對象都包含一個指向構造函數的指針, 而實例(instance)都包含一個指向原型對象的內部指針.
咱們先來看一個例子java
function Person(name, age, job) { this.name = name this.age = age this.job = job this.sayName = function() { alert(this.name) } } var person1 = new Person('Zaxlct', 28, 'Engineer') var person2 = new Person('Mick', 23, 'Doctor')
上面的例子中 person1 和 person2 都是 Person 的實例。這兩個實例都有一個 constructor (構造函數)屬性,該屬性(是一個指針)指向 Person。 即:webpack
console.log(person1.constructor == Person) //true console.log(person2.constructor == Person) //true
每一個構造函數都有一個 prototype 屬性,指向調用該構造函數而建立的實例的原型,也就是這個例子中的 person1 和 person2 的原型。git
function Person() {} Person.prototype.name = 'Zaxlct' Person.prototype.age = 28 Person.prototype.job = 'Engineer' Person.prototype.sayName = function() { alert(this.name) } var person1 = new Person() person1.sayName() // 'Zaxlct' var person2 = new Person() person2.sayName() // 'Zaxlct' console.log(person1.sayName == person2.sayName) //true
這是每個 JavaScript 對象(除了 null )都具備的一個屬性,叫proto,這個屬性會指向該對象的原型。es6
function Person() {} var person1 = new Person() console.log(person1.__proto__ === Person.prototype) // true
每一個原型都有一個 constructor 屬性指向關聯的構造函數github
function Person() {} var person1 = new Person() console.log(Person === Person.prototype.constructor) // true console.log(person1.__proto__ === Person.prototype) // true
當讀取實例的屬性時,若是找不到,就會查找與對象關聯的原型中的屬性,若是還查不到,就去找原型的原型,一直找到最頂層爲止。web
function Person() {} Person.prototype.name = 'Kevin' var person = new Person() person.name = 'Daisy' console.log(person.name) // Daisy delete person.name console.log(person.name) // Kevin
在這個例子中,咱們給實例對象 person 添加了 name 屬性,當咱們打印 person.name 的時候,結果天然爲 Daisy。面試
可是當咱們刪除了 person 的 name 屬性時,讀取 person.name,從 person 對象中找不到 name 屬性就會從 person 的原型也就是 person.proto ,也就是 Person.prototype 中查找,幸運的是咱們找到了 name 屬性,結果爲 Kevin。
語法:Object.create(proto, [propertiesObject])
方法建立一個新對象,使用現有的對象來提供新建立的對象的 proto。
// new Object() 方式建立 var a = { rep: 'apple' } var b = new Object(a) console.log(b) // {rep: "apple"} console.log(b.__proto__) // {} console.log(b.rep) // {rep: "apple"} // Object.create() 方式建立 var a = { rep: 'apple' } var b = Object.create(a) console.log(b) // {} console.log(b.__proto__) // {rep: "apple"} console.log(b.rep) // {rep: "apple"}
經典面試題
var obj1 = { name: 'one' } obj2 = Object.create(obj1) obj2.name = 'two' console.log(obj1.name) //one var obj1 = { prop: { name: 'one' } } obj2 = Object.create(obj1) obj2.prop.name = 'two' console.log(obj1.prop.name) //two var obj1 = { list: ['one', 'one', 'one'] } obj2 = Object.create(obj1) obj2.list[0] = 'two' console.log(obj1.list[0]) //two
在函數內部定義的變量和函數若是不對外提供接口,那麼外部將沒法訪問到,也就是變爲私有變量和私有函數。
function Obj() { var a = 0 //私有變量 var fn = function() { //私有函數 } } var o = new Obj() console.log(o.a) //undefined console.log(o.fn) //undefined
當定義一個函數後經過 「.」爲其添加的屬性和函數,經過對象自己仍然能夠訪問獲得,可是其實例卻訪問不到,這樣的變量和函數分別被稱爲靜態變量和靜態函數。
function Obj() {} Obj.a = 0 //靜態變量 Obj.fn = function() { //靜態函數 } console.log(Obj.a) //0 console.log(typeof Obj.fn) //function var o = new Obj() console.log(o.a) //undefined console.log(typeof o.fn) //undefined
在面向對象編程中除了一些庫函數咱們仍是但願在對象定義的時候同時定義一些屬性和方法,實例化後能夠訪問,JavaScript也能作到這樣。
function Obj(){ this.a=[]; //實例變量 this.fn=function(){ //實例方法 } } console.log(typeof Obj.a); //undefined console.log(typeof Obj.fn); //undefined var o=new Obj(); console.log(typeof o.a); //object console.log(typeof o.fn); //function
題目以下
function Foo() { getName = function() { alert(1) } return this } Foo.getName = function() { alert(2) } Foo.prototype.getName = function() { alert(3) } var getName = function() { alert(4) } function getName() { alert(5) } //請寫出如下輸出結果: Foo.getName() getName() Foo().getName() getName() new Foo.getName() new Foo().getName() new new Foo().getName()
解讀:首先定義了一個叫 Foo 的函數,以後爲 Foo 建立了一個叫 getName 的靜態屬性存儲了一個匿名函數,以後爲 Foo 的原型對象新建立了一個叫 getName 的匿名函數。以後又經過函數變量表達式建立了一個 getName 的函數,最後再聲明一個叫 getName 函數。
先來劇透一下答案,再來看看具體分析
//答案: Foo.getName() //2 getName() //4 Foo().getName() //1 getName() //1 new Foo.getName() //2 new Foo().getName() //3 new new Foo().getName() //3
1.第一問:Foo.getName 天然是訪問 Foo 函數上存儲的靜態屬性,天然是 2
2.第二問,直接調用 getName 函數。既然是直接調用那麼就是訪問當前上文做用域內的叫 getName 的函數,因此跟 1 2 3 都沒什麼關係。可是此處有兩個坑,一是變量聲明提高,二是函數表達式。
關於函數變量提示,此處省略一萬字。。。。題中代碼最終執行時的是
function Foo() { getName = function() { alert(1) } return this } var getName //只提高變量聲明 function getName() { alert(5) } //提高函數聲明,覆蓋var的聲明 Foo.getName = function() { alert(2) } Foo.prototype.getName = function() { alert(3) } getName = function() { alert(4) } //最終的賦值再次覆蓋function getName聲明 getName() //最終輸出4
3.第三問的 Foo().getName(); 先執行了 Foo 函數,而後調用 Foo 函數的返回值對象的 getName 屬性函數。這裏 Foo 函數的返回值是 this,this 指向 window 對象。因此第三問至關於執行 window.getName()。 然而這裏 Foo 函數將此變量的值賦值爲function(){alert(1)}
。
4.第四問直接調用 getName 函數,至關於 window.getName(),答案和前面同樣。
5.後面三問都是考察 js 的運算符優先級問題。
總結前端性能優化的方法
幾種常見的JS遞歸算法
搭建一個vue-cli的移動端H5開發模板
封裝一個toast和dialog組件併發布到npm
從零開始構建一個webpack項目
總結幾個webpack打包優化的方法
一文讀盡前端路由、後端路由、單頁面應用、多頁面應用
關於幾個移動端軟鍵盤的坑及其解決方案
淺談JavaScript的防抖與節流