知道函數有原型對象javascript
構造函數,原型對象和實例三者的關係java
原型鏈面試
會給內置的對象添加自定義的方法數組
會使用更簡單的原型使用方式瀏覽器
####1.1.1 全局做用域函數
整個js執行的環境就是一個全局做用域ui
####1.1.2 局部做用域this
es5規範中: 只有函數才能構成一個局部做用域es5
####1.1.3 做用域鏈spa
將js執行時變量查找的方式,以鏈式形式表示出來
var num = 0;
function fn(){
var num1;
num1 = 1;
console.log(num1);
function fnSon(){
var num2 = 2;
console.log(2)
}
}
複製代碼
將上面的代碼用鏈式的形式展現出來
詞法做用域又叫靜態做用域.
做用域是在代碼書寫完畢以後就造成了,與代碼執行無關
內部做用域能夠訪問外部做用域的變量,可是外部不能夠訪問內部的
函數的形參就至關於在當前函數的做用域中申明瞭這個變量
訪問變量時,先在本身的做用域中查找,若是沒有則沿着做用域鏈往上找,直到全局.若是全局也沒有就報錯
給變量賦值以前,要先找變量.查找變量也是沿着做用域鏈查找,直到全局,若是全局也沒有,則會再全局做用域建立這個變量(隱式全局)
代碼執行以前先考慮預解析規則,調用函數時,執行函數裏的代碼以前,函數內也要先執行預解析規則
###面試題:
var a;
if ("a" in window) {
var a = 10;
}
alert(a); //
//=============================================
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
alert(foo); //
}
bar();
//================================================
var num = 123;
function f1(num) {
console.log(num); //
}
function f2() {
var num = 456;
f1(num);
}
f2();
//======================================================
function fn(){
var a = 1, b = 1, c = 1;
}
fn();
console.log(c); //
console.log(b); //
console.log(a); //
function fn1(){
var a = b = c = 1;
}
fn1();
console.log(c); //
console.log(b); //
console.log(a); //
//========================================================
var a = 1;
function fn(){
var a = 2;
function fnSon(a){
a = 3;
console.log(a); //
}
fnSon();
console.log(a); //
}
console.log(a); //
fn();
console.log(a); //
//==========================================================
var a ;
function a(){
console.log('呵呵')
function a(){
a = 4;
console.log('哈哈')
}
a();
console.log(a); //
}
a();
console.log(a); //
//=================================================================
var a = 1;
function a(){
a++;
}
console.log(a) //
//==================================================================
var a = { x : 1 }
var b = a;
a.x = a = { n : 1};
console.log(a.x); //
console.log(b.x); //
//本身把代碼運行下,看看和本身想的結果有啥差異
複製代碼
###1.3. 建立對象的方式
####1.3.1 簡單方式
咱們能夠直接經過 new Object()
建立:
var person = new Object()
person.name = 'Jack';
person.age = 18;
person.sayName = function () {
console.log(this.name); //jack
}
複製代碼
每次建立經過 new Object()
比較麻煩,因此能夠經過它的簡寫形式對象字面量來建立:
字面量形式的建立方式,底層也是new Object建立出來的
var person = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name);
}
}
複製代碼
上面的寫法比較簡單,可是若是咱們要建立多個person對象呢?
var person1 = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name);
}
}
var person2 = {
name: 'Mike',
age: 16,
sayName: function () {
console.log(this.name);
}
}
var person3 = {
name: 'zs',
age: 17,
sayName: function () {
console.log(this.name);
}
}
複製代碼
經過上面的代碼咱們不難看出,這樣寫的代碼太過冗餘。
####1.3.2 簡單方式的改進:工廠函數
咱們能夠寫一個函數,解決代碼重複問題:
function createPerson (name, age) {
return {
name: name,
age: age,
sayName: function () {
console.log(this.name);
}
}
}
複製代碼
而後生成對象:
var p1 = createPerson('Jack', 18);
p1.sayName(); //jack
var p2 = createPerson('Mike', 18);
p2.sayName(); // Mike
複製代碼
####1.3.3 更優雅的方式(更推薦使用的一種方式):構造函數
一種更優雅的工廠函數就是下面這樣,構造函數:
function Person (name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
}
}
var p1 = new Person('Jack', 18);
p1.sayName() // => Jack
var p2 = new Person('Mike', 23);
p2.sayName() // => Mike
複製代碼
**構造函數中new關鍵字作什麼事在上一篇博客裏寫的很詳細有須要能夠看看
使用構造函數帶來的最大的好處就是建立對象更方便了,可是其自己也存在一個浪費內存的問題:
function Person (name, age) {
this.name = name;
this.age = age;
this.sayHello = function () {
console.log('hello ' + this.name);
}
}
var p1 = new Person('Tom', 18);
var p2 = new Person('Jack', 16);
複製代碼
以上代碼的圖示:
經過上面的圖示,咱們發現,每個對象都引用了一個函數,咱們建立了多少個對象,對應的就會在內存中建立出對應數量的同樣的函數.這樣形成了內存的極大浪費
function Person (name, age) {
this.name = name;
this.age = age;
this.sayHello = say;
}
function say (){
console.log('hello ' + this.name);
}
var p1 = new Person('Tom', 18);
var p2 = new Person('Jack', 16);
p1.sayHello(); // hello Tom
p2.sayHello(); // hello Jack
複製代碼
**注意: ** 這種方式,能夠解決構造函數浪費內存的問題,可是,同時又出現了一個新的問題,咱們把函數定義在了全局,
全局的函數,很容易被別人寫的代碼覆蓋.
###3.2 利用函數的原型對象(更優雅的解決方案)
js給每個函數,提供了一個對應的原型對象.能夠經過函數的prototype屬性訪問到這個原型對象.
原型對象有一個constructor的屬性會指向本身對應的函數
而咱們經過
new 函數
建立出來的實例對象,默承認以訪問到函數對應的原型對象上的屬性
function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function (){
console.log('hello ' + this.name);
}
var p1 = new Person('Tom', 18);
var p2 = new Person('Jack', 16);
p1.sayHello(); // hello Tom
p2.sayHello(); // hello Jack
複製代碼
**注意: **爲了咱們方便查看,實例和原型的關係.瀏覽器很貼心的幫咱們實現了一個屬性
__proto__
, 經過這個屬性,咱們能夠在控制檯上清楚的看到原型.可是
__proto__
不是w3c標準的屬性,因此不要在生產環境(上線)下使用.
function Person (name, age) {
this.name = name;
this.age = age;
}
var p1 = new Person();
console.log(Perosn.prototype === p1.__proto__) //true
複製代碼
利用原型對象,能夠更加優雅的解決構造函數浪費內存的問題
通常對象私有的屬性直接寫在構造函數中,而對象公有的屬性寫在原型對象上
函數對應有一個本身的原型對象 , 經過prototype屬性能夠訪問
原型對象有一個constructor屬性,能夠指回本身的對應的函數
經過函數new出來的實例,默承認以訪問到原型對象的屬性 ,咱們能夠經過__proto__
在控制檯看到
原型對象也是對象,那麼這個對象的是被誰建立出來的呢?
function Person (name, age) {
this.name = name;
this.age = age;
}
var p1 = new Person();
console.log(Person.prototype) // 指向Person的原型對象
console.log(Person.prototype.__proto__.constructor) // 咱們能夠看到Person的原型對象是Object的實例
複製代碼
由於這個查找規則,因此Object函數原型對象上的全部屬性均可以被其餘對象訪問到
function Student(){}
var s1 = new Student();
s1.toString() //[object Object]
複製代碼
Array.prototype
String.prototype
...
經過觀察內置函數的原型,咱們發現咱們在數組/字符串經常使用的API,其實都定義在他們對應的原型上的.因此全部的數組對象/字符串,都能使用這些API.
##6. 更簡單的原型使用方式
若是咱們有不少公用的屬性,那麼一個一個的添加在函數的原型上就比較麻煩,咱們還能夠有一種更簡單的方式
直接新建一個對象賦值給函數的prototype屬性
function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person, // => 手動定義一個 constructor 屬性, 指向正確的構造函數
sayHello: function () {
console.log('我叫' + this.name + ',我今年' + this.age + '歲了');
}
eat : function(){
console.log('吃飯ing...');
}
}
var p1 = new Person('zs', 18);
複製代碼