JavaScript進階入門(一) 預解釋做用域this

1 JavaScript有哪些數據類型

1.1 基本數據類型

number、string、boolean、null、undefined javascript

1.2 引用數據類型

object:{}、[ ]、/$/、Date
function html

2 全局做用域(global/window)

當瀏覽器加載html頁面的時候,首先會提供一個全局jsdiam執行的環境java

var num = 12;
var obj = {name:"wjw",age:7);
function fn(){
	console.log("勿忘初心方得始終")
}
console.log(fn); // 把整個函數的定義部分(函數自己在控制檯輸出)
console.log(fn()); //把當前函數執行的返回結果(return後面寫的是啥,返回就是啥,若是沒有return,默認返回undefined)
複製代碼

image.png

3 預解釋(變量提'前'聲'明')

在當前的做用域中,js代碼執行以前,瀏覽器首先會默認的把全部帶var和function的進行提早的聲明或者定義瀏覽器

3.1 聲明(declare)

var num ;
複製代碼

告訴瀏覽器在全局做用域中有一個num的變量了
若是一個變量只是聲明瞭,可是沒有進行變量的定義,那就是undefined
bash

3.2 定義(define)

num = 12 //給咱們的變量進行賦值
複製代碼

3.3 var

在預解釋的時候只是提早的聲明閉包

3.4 function

在預解釋的時候,提早的聲明+定義都完成了dom

對於帶var和function關鍵的預解釋的操做仍是不同函數

fn(100,200);//能夠在上面執行,由於預解釋的時候,聲明+定義已經完成了

function fn(num1,num2){
	var total = num1 + num2;
  console.log(total);
}
複製代碼

3.5 預解釋只發生在當前的做用域之下

例如:開始只對window下得進行預解釋,只有函數執行的時候纔會對函數的進行預解釋ui

image.png

4 js中內存的分類

4.1 棧內存

用來提供一個js執行的環境 -> 做用域(全局的做用域,私有)this

4.2 堆內存

用來存儲引用數據類型的值 -> 對象存儲屬性名和屬性值,函數存儲的是代碼字符串

5 如何區分私有變量和全局變量?

  1. 在全局做用域下聲明(預解釋的時候)的變量就是全局變量
  2. 在私有做用域中聲明變量和函數的形參都是私有的變量

在私有做用域中,咱們代碼執行的時候遇到一個變量,首先咱們須要肯定當前它,是不是私有變量,若是是私有的變量,那麼和外面的任何東西都沒有關係;若是不是私有的,則往當前做用域上級做用域進行查找,若是上級做用域也沒有則繼續查找,一直找到window爲止..

6 當函數執行的時候

(直接目的:讓函數體重的代碼執行),首先會造成一個新的私有的做用域

1.若是有形參,先給形參賦值
  2.進行私有做用域中的預解釋 
  3.私有做用域中的代碼從上到下執行

console.log(total); // undefined
var total = 0;
function fn(num1,num2){
	console.log(total); // total 不是私有的,找全局下的total,也就是在這裏出現全部的total其實應該都是
  var total = num1 + num2;// 全局的total=300
  console.log(total);// 300
}
fn(100,200);
console.log(total);// 300
複製代碼

7 閉包

函數造成一個新的私有的做用域保護了裏面的私有變量不受外界的干擾(修改不了私有的,私有的也修改不了外面的

在全局做用域中,帶var和不帶var的關係?

區別:帶var的能夠進行預解釋,因此在賦值的前面執行不會報錯;不帶var的是不能進行預解釋的,在前面執行會報錯

console.log(num);//undefined
var num = 12;
console.log(num2); // Uncaught ReferenceError:num2 is no defined
num2 = 12;
複製代碼

關係:num = 12 ->至關於給window增長了一個叫作num的屬性名,屬性值12
var num =12 ->首先它至關於給全局做用域增長了一個全局變量num,可是不只如此,它也至關於給window增長了一個屬性名怒,屬性值12

var num = 12;
console.log(num); // 12
num2 = 12;
console.log(num2);// 12 window.num2
複製代碼

私有做用域中出現的一個變量不是私有的,則往上級做用域進行查找,上級沒有則繼續向上級查詢,一直找到window爲止

funtciont fn(){
	console.log(total1); // Uncaught ReferenceError:num2 is no defined
  total = 100;
}
fn();
console.log(total1)
複製代碼

js中若是不進行任何特殊處理的狀況下,上面的代碼報錯,下面的代碼都不在執行了


8 預解釋的解析

in "num" in window 判斷num是否爲window這個對象的一個屬性,是的話返回true,不是返回flase

console.log("name" in obj)
複製代碼

8.1  預解釋的時候無論你的條件是否成立,都要把var的進行提早的聲明

window的預解釋:var num - > window num

if(!("num") in window){  // 「num」 in window ->true

    var num = 12

}
console.log(num) -> undefind
複製代碼

8.2  預解釋的時候只預解釋「=」左邊的,右邊的是值,不參與預解釋

匿名函數值函數表達式:把函數定義跌部分當作一個值賦值給咱們的變量(元素的某一個事件)
window下的預解釋 var fn;

fn(); // undefined

var fn = function(){

    conslole.log("ok")

}

fun(); // ok

function fn(){
  console.log("ok")
}

fn(); // ok
複製代碼

8.3 執行函數定義的那個function在全局做用下不進行預解釋,當代名執行到這個位置的時候,和執行一塊兒完成

自執行函數: 定義和執行的一塊兒完成了

(function (params) {})(100);

~function (params) {}(100);

+function (params) {}(100);

-function (params) {}(100);

!function (params) {}(100);
複製代碼

8.4 函數體中return 下面的代碼雖然不在執行,可是須要進行預解釋,跟着都返回給咱們的值,因此不進行預解釋

function fn(){

    // 預解釋:var num;

    console.log(num); // ->undefined

    return function(){}; // -> 不預解釋

    var num = 100

}

fun();
複製代碼

8.5 在js中若是變量的名字和函數的名字重複

預解釋:var fn ; window.fn; fn-xxxfff000 window.fn-xxxfff00

var fn = 13;

function fn() {
 console.log("ok");
}
複製代碼

window 預解釋 聲明+定義 fn = xxxfff11 聲明 var fn; (不須要從新聲明) 聲明(不重複進行)+定義 fn = xxxfff222 fn = xxxfff22

fn(); // ->2

function fn() {console.log(1)}; //-> 2

fn(); // 2

var fn = 10;

fn (); // error 終止了

function fn() {console.log(2)};

fn();
複製代碼

9 如何查找上級做用域

看當前函數是哪一個做用域下定義的,那麼它的上級做用域就是誰

9.1 代碼

let num =12;
function fn() {
    var num = 120;
    return function () {
        console.log('====================================');
        console.log(num);
        console.log('====================================');
    }
}
var f = fn(); 
f(); // 這樣就執行了第1次
~function() {
    var num = 1200;
    f(); // 這樣就執行了第2次
}()
複製代碼

9.2 運行結果

var f = fn()就是把函數fn的運行返回結果賦值給f,fn函數返回的是一個函數,該返回函數的上級做用域爲函數fn
的私有做用域,因此運行f函數console.log(num),num不是f私有做用域的私有變量,須要在上級做用域找,fn的私有做用域含有變量
num,因此都輸出的num爲120。

image.png

10 自執行匿名函數

  • 常見格式:(function(){ /* code */ })();
  • 解釋:包圍函數(function(){ /* code */ })的第一對括號向腳本返回未命名的函數,隨後一對空括號當即執行返回的未命名函數,括號內爲匿名函數的參數。
  • 做用:能夠用它建立命名空間,只要把本身的全部代碼都寫在這個特殊的函數包裝內,那麼外部就不能訪問,除非你容許(變量前加上window,這樣該函數後變量就成爲全局)。各JavaScript庫的代碼也基本上是這種組織形式。

總結一下,執行函數的做用主要爲匿名和自動執行,代碼在被解釋時就已經在運行了。

(function(){ /* code */ }());

!function(){ /* code */ }(); 

~function(){ /* code */ }();

-function(){ /* code */ }(); 

+function(){ /* code */ }();


~function(a) {

    console.log('====================================');

    console.log(a);

    console.log('====================================');

}(10)
複製代碼

11 關於內存釋放和做用域銷燬的研究

11.1 堆內存

對象數據類型或者函數數據類型在定義的時候首先都會開闢一個堆內存,堆內存有一個引用的地址,若是外面有變量等知道這個地址,外面就說這個內存被佔用,就不能銷燬。

var obj1 = {name:"張三"};
var obj2 = obj1;
複製代碼

咱們想要讓堆內存釋放/銷燬,只須要吧全部引用它的變量值賦值爲null便可,若是當前的堆內存沒有任何東西唄佔用了,那麼瀏覽器會在空間的時候把它銷燬...

obj1 = null;
obj2 = null;
複製代碼

11.2 棧內存

11.2.1 全局做用域

只有當頁面關閉的時候全局做用域纔會銷燬

11.2.2 私有的做用域

通常狀況下,函數執行會造成一個新的私有的做用域,當私有做用域中的代碼執行完成後,咱們當前做用域都會主動的進行釋放和銷燬
可是仍是存在特殊的狀況的:
當前私有做用域中的部份內存被做用域之外的東西佔用了,那麼當前的這個做用域就不能銷燬了

a.函數執行返回了一個引用數據類型的值,而且在函數的外面被一個其餘的東西給接收了,這種狀況下造成的私有做用域都不會銷燬

function fn(){
		var num = 100;
    return function(){
    	num++;
      console.log(num);
    }
}
var f = fn(); // fn執行造成這個私有的做用域就不能再銷燬了
複製代碼


b.在一個私有的做用域中給dom元素的事件綁定方法,通常狀況下咱們的私有做用域都不會銷燬

var oDLv = document.getElementById('div1');
~function(){
	oDLv.onclick = function(){
  
  } 
}() // 當前自執行函數造成一個私有的做用域也不銷燬
複製代碼

c.下述狀況屬於不當即銷燬->fn返回的函數沒有被其它的東西佔有,可是還須要執行一次呢,因此暫時不銷燬,當返回的值執行完成後,瀏覽器會在空閒的時候把它銷燬

function fn(){
	var num = 100;
  return function(){
  
  }
}
複製代碼

12 做用域練習題

都是自身累加1,在和其餘的值進行運算的時候是有區別的

  • i++: 先拿i的值進行運算,運算完成自己+1
  • ++i: 先自己累加1,而後拿累加完成的結果去運算
var i = 5;
console.log(1+i++);//6 i=6
console.log(1+(++i));//6 i=7
console.log(2+(i++)+(++i)+(++i)+(i++));//38
console.log(i);// 9
複製代碼
function fn(){
	var i = 10;
  return function (n){
  	console.log(n+(++i));
  }
} 
var f = fn();
f(10);// 21
f(20);// 32
fn()(10);// 21
fn()(20);// 31
複製代碼
function fn(){
	var i = 10;
  return function (n){
  	console.log(n+(++i));
  }
} 
var f = fn(13);
f(12);// 25
f(24);// 28
fn(15)(12);// 27
fn(16)(13);// 29
複製代碼

13 this關鍵字

咱們在js中主要研究的都是函數中的this
js中的this表明的是當前行爲執行的主體,js中的context表明的是當前行爲的環境(區域)
例如:吳佳瑋在沙縣吃蛋炒餅,this->吳佳瑋 context->沙縣小吃

function 吃飯(){
	this->吳佳瑋
}
吳佳瑋.吃飯();

~function(){
	吳佳瑋.吃飯();
}();
複製代碼

this是誰和函數在哪定義的和在哪執行的都沒有任何關係:如何的區分 this呢

  1. 函數執行,首先看函數名前是否有".",有的話"."前面是誰this是誰;沒有的話this是window
function fn(){
	console.log(this);
}
var obj ={
	fn:fn
};
fn(); // this->window
obj.fn();// this->obj

function sum(){
	fn();
}
sum();
複製代碼
  1. 自執行函數中的this永遠是window
  2. 給元素的某一個時間綁定方法,當時間觸發的時候,執行對應的方法,方法中的this是當前的元素

<div id="div1">有本事點我啊~~</div>

...
<script> document.getElementById("div1").onclick = fn;//fn中的this是#div1 document.getElementById("div1").onclick = fucntion(){ // this->#div1 fn(); // this->window } </script>
複製代碼

14 綜合練習題

var num = 20;
var obj = {
	num:30,
  fn:(function(num){
  		this.num *=3;
      num+=15;
      var num = 45;
      return function(){
      	this.num *=4;
        num+=20;
        console.log(num);
      }
  })(num) // 全局變量num的值20賦值給自執行函數的形參,而不是obj下的30,若是想是obj下的30,咱們須要寫obj.num
}
var fn = obj.fn;
fn();
obj.fn();
複製代碼

image.png

var oBth = document.getElementById("btn");
var spanNum = document.getElementById("spanNum")

// 利用全局做用域不銷燬原理,把須要的數字定義爲全局的變量
var count = 0;
oBtn.onclick = fucntion(){
  count++;
  spanNum.innerHTML = cont;
}
// 弊端:在項目中爲防止全局變量之間衝突,咱們通常是禁止或者減小使用全局變量的使用
// 本身造成一個不銷燬的私有做用域來保存咱們須要累加的數字
複製代碼

二、 利用innerHTML的方式處理,每一次點擊時候都先到頁面中獲取最新的值,而後累加,最後把累加的結果從新放回去

var oBth = document.getElementById("btn");
var spanNum = document.getElementById("spanNum")

//
oBtn.onclick = fucntion(){
  spanNum.innerHTML++;
}
// 弊端:每一次吧頁面中的內容先轉換爲字符串而後再累加,累加玩以後從新添加回去,
// 當從新的添加的時候瀏覽器重新的渲染一下
複製代碼

三、利用自定義屬性存儲(推薦)

oBth.count = 0;
oBth.onclick = function(){
	// spanNum.innerHTML獲取頁面中的內容返回的是一個字符串
  spanNum.innerHTML = ++this.count;
}
複製代碼
相關文章
相關標籤/搜索