1. 程式人生 > 其它 >JS知識點+試題(二)

JS知識點+試題(二)

作用域

 

 

 

迴圈陷阱

什麼是迴圈陷阱?

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
  • 由於匿名函式中使用的變數 i 在作用域外宣告形成閉包
  • i 屬於全域性作用域
  • 所以迴圈中建立的匿名函式都指向同一個變數

迴圈陷阱就是看似每個新建立的函式都需要一個單獨的變數,但沒有實現

解決:

A.IIFE即時執行函式解決
var a = [];
for (var i = 0; i < 10; i++) {
  (function
(n) {    a[i] = function () {    console.log(n); }; })(i) } a[6](); // 6

B.ES6 使用let ——塊級作用域

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

問:為什麼塊級作用域存在並使用?

答:在Javascript提出塊級作用域,主要為了解決一個重要的設計缺陷 。
> 變數提升特性導致的大量與直覺不符的程式碼   SO,變數提升是什麼?
(function
() { console.log(v) var v = 123 })()//undefined // 變數提升後 (function() { var v console.log(v) v = 123 })()
JS在變數宣告提升的時候會將 var 宣告的變數以及 用關鍵字函式宣告的函式都會提升到當前作用域的頂端 。賦值語句在原地等待賦值。 優點是降低程式的編寫難度,無明確順序要求。
var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); 
// undefined

問:let是否會造成變數提升

var a = 8;
(function() {
  // let a 此時暫時性死區開始
  console.log(a); // Uncaught ReferenceError: x is not defined
  //暫時性死區結束
  let a = 1
}())
//報錯

導致現象:

暫時性死區 (Temporal Dead Zone)
引用MDN上的定義
> let bindings are created at the top of the (block) scope containing the declaration, commonly referred to as “hoisting”. Unlike variables declared with var, which will start with the value undefined, let variables are not initialized until their definition is evaluated. Accessing the variable before the initialization results in a ReferenceError. The variable is in a “temporal dead zone” from the start of the block until the initialization is processed.
大概意思便是let同樣存在變數提示(hoisting),只是形式與var不同,var定義的變數將會被賦予undefined的初始值,而let在被顯式賦值之前不會被賦予初始值,並且在賦值之前讀寫變數都會導致 ReferenceError 的報錯。從程式碼塊(block)起始到變數求值(包括賦值)以前的這塊區域,稱為該變數的暫時性死區。 BUT,紅寶書沒有這樣的敘述   答:不做判斷型回答- 陳述客觀事實    - 程式碼現象   - MDN與紅寶書記述

 

this指向

function say() {
  console.log("我的家鄉", this.name);
}
var bj = {
  name:'北京',
  say
}
window.name = "中國";
say(); // 我的家鄉 中國
bj.say();// 我的家鄉 北京

1.

普通函式

如果一個函式不屬於任何一個物件,上下文就是全域性物件。
在瀏覽器中就是window,如果是node執行時中就是global 2. 物件屬性
接著說一下在物件中的this指向。this指向在物件中指向物件例項。其實歸屬某一個物件才是上下文環境使用最多的情況。同樣一個方法可以根據物件例項中資料成員的不同產生不同的變化。 3. 建構函式與Class
類是物件例項,可以認為是例項的模板。
比如一個國家模板,例項一個國家比如中國。
當然國家模板中的say方法指向的建立的物件例項china    
function say() {
  console.log("我的家鄉:", this.name);
}
function Country(name) {
  this.name = name
}
Country.prototype.say = say
const china = new Country('中國')
china.say() //我的家鄉: 中國

ES6 

class關鍵字同理,建構函式constructor等同於上文中的Country建構函式
class Country {
  constructor(name) {
    this.name = name
  }
  say() {
    console.log("我的家鄉:", this.name);
  }
}
const china = new Country('中國')
china.say()

 

4. call、apply、bind方法中
function say() {
  console.log('我的家鄉:' + this.name)
}
window.name = '中國'
say()
say.apply({name : '北京'})
say.call({name : '朝陽'})
var mySay = say.bind({name: '曹縣'}) mySay()

 

嚴格模式下this
//函式外指向window

"use strict"
console.log(this) // window
function foo() {
  console.log(this) // undefined
}
foo()

 


箭頭函式
在箭頭函式中,this總是指向詞法作用域,也就是外層呼叫者。而不是方法所在物件。
function say() {
  console.log("我的家鄉在"+this.name);
}
var bj = {
  name: "北京",
  say: () => say(),
  chaoyang: {
    name: "朝陽",
    say : () => say(), // 修改為箭頭函式
  },
};
name = '中國'
bj.say(); // 我的家鄉在中國
bj.chaoyang.say(); // 我的家鄉在中國