1. 程式人生 > >JavaScript學習筆記(一)//更新中

JavaScript學習筆記(一)//更新中

JavaScript學習筆記 - 題目練習

Learning Advanced JavaScript

Reference: Learning Advanced JavaScript
Courses: Web2.0 Programming.
Tools: Repl.it

#2: Goal: To be able to understand this function:

// The .bind method from Prototype.js 
Function.prototype.bind = function(){ 
  var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); 
  return function(){ 
    return fn.apply(object, 
      args.concat(Array.prototype.slice.call(arguments))); 
  }; 
};

上述程式碼中,

prototype是函式的一個屬性,並且是函式的原型物件。引用它的必然是函式。call是函式的一個方法,關於這個方法,它也是隻有函式才能夠呼叫的,它的作用是:呼叫引用它的函式。slice(start[,end])是js的一個原生陣列函式,作用是獲取陣列中從start下標開始到end下標結束的元素。 arguments是js函式物件的一個屬性,作用是獲取函式的實參,返回的是一個以函式實參為屬性元素的物件。而Array是js中生成陣列的關鍵字,陣列的關鍵字Array不能這樣子Array.xx直接呼叫js的陣列函式,而是要用Array.prototype來呼叫陣列函式。(給原型物件增加屬性,也就是給物件增加公用的屬性)Array.prototype是一個數組,String.prototype是一個字串,Object.prototype是一個物件。
reference.

args=Array.prototype.slice.call(arguments)將呼叫bind函式時引數集合arguments轉換為陣列array。

object=args.shift()將args陣列第一個元素取出作為當前物件

匿名函式中,呼叫args.concat(Array.prototype.slice.call(arguments))是為了將呼叫匿名函式時傳入的引數與呼叫bind時引數合併成一個引數陣列

以一個呼叫示例來看上述過程:

var obj = { x: 'prop x' };
//args = Array.prototype.slice.call(arguments)後args = [obj, 12, 23 ]
//object=args.shift()後,args =[12, 23] ,object =obj

var boundExample = example.bind(obj, 12, 23); 
boundExample(36, 49); // arguments => 36, 49 ,呼叫args.concat(Array.prototype.slice.call(arguments))後,arguments that our example() function receives => [12, 23, 36, 49]

參考連結

#3: Some helper methods that we have:

assert( true, "I'll pass." ); //undefined
assert( "truey", "So will I." ); //undefined
assert( false, "I'll fail." ); //{ [AssertionError [ERR_ASSERTION]: I'll fail.]
  //generatedMessage: false,
  //name: 'AssertionError [ERR_ASSERTION]',
  //code: 'ERR_ASSERTION',
  //actual: false,
  //expected: true,
  //operator: '==' }
assert( null, "So will I." ); //{ [AssertionError [ERR_ASSERTION]: So will I.]
  //generatedMessage: false,
  //name: 'AssertionError [ERR_ASSERTION]',
  //code: 'ERR_ASSERTION',
  //actual: null,
  //expected: true,
  //operator: '==' }
log( "Just a simple log", "of", "values.", true ); //ReferenceError: log is not defined
error( "I'm an error!" ); //ReferenceError: error is not defined
// Terminal node v10.13.0

判斷值是否為真值有以下兩個斷言測試函式

  1. assert(value[, message])
    這個測試函式在 【Boolean(value)】 為 【true】時通過斷言測試,否則丟擲 【AssertionError】。( value 為false,則丟擲一個帶有 message 屬性的 【AssertionError】,其中 message 屬性的值等於傳入的 message 引數的值。 如果 message 引數為 undefined,則賦予預設的錯誤資訊。)
  2. assert.ok(value[, message])
    assert.ok() 與 assert()的作用是一樣的,都是測試【value】是否為真值。而且用法也一樣,所以可以將assert()視為assert.ok()的語法糖

參考連結

#5: What ways can we define functions?

function isNimble(){ return true; } 
var canFly = function(){ return true; }; 
window.isDeadly = function(){ return true; }; 
log(isNimble, canFly, isDeadly);

#6: Does the order of function definition matter?

var canFly = function(){ return true; }; 
window.isDeadly = function(){ return true; }; 
assert( isNimble() && canFly() && isDeadly(), "Still works, even though isNimble is moved." ); 
function isNimble(){ return true; }

函式可定義在任何地方,次序不重要,javascript在執行之前會將所有的變數和函式進行升級(define promotion)。

#7: Where can assignments be accessed?

assert( typeof canFly == "undefined", "canFly doesn't get that benefit." ); //[AssertionError [ERR_ASSERTION]: canFly doesn't get that benefit.]
assert( typeof isDeadly == "undefined", "Nor does isDeadly." ); 
var canFly = function(){ return true; }; //undefined
window.isDeadly = function(){ return true; };

如果是賦值方式定義函式指標的話,必須得在呼叫該函式指標之前定義,否則將無法訪問。在javascript中函式的定義和變數的定義都為提升到global域當中,但變數只有定義會提升,其值並不提升,因此你會看到undefined。賦值的命名函式其函式名只在函式內部可以識別出.。

提升(Hoisting)是 JavaScript 預設將當前作用域提升到前面去的的行為。
提升(Hoisting)應用在變數的宣告與函式的宣告。
使用表示式定義函數時無法提升。
eg. var x = function (a, b) {return a * b}; //表示式宣告
JavaScript 函式可以通過一個表示式定義。函式表示式可以儲存在變數中;在函式表示式儲存在變數後,變數也可作為一個函式使用;實際上是一個匿名函式 (函式沒有名稱)。函式儲存在變數中,不需要函式名稱,通常通過變數名來呼叫。

#8: Can functions be defined below return statements?

function stealthCheck(){ 
  assert( stealth(), "We'll never get below the return, but that's OK!" ); 
 
  return stealth(); 
 
  function stealth(){ return true; } 
} //undefined
 
stealthCheck();//true

stealthCheck函式在執行之前會對所有內部的函式和變數進行升級。

#10: We can refer to a function, within itself, by its name.

function yell(n){ 
  return n > 0 ? yell(n-1) + "a" : "hiy"; 
} //yell(4)=='hiyaaaa'
assert( yell(4) == "hiyaaaa", "Calling the function by itself comes naturally." ); 

#11: What is the name of a function?

var ninja = function myNinja(){ 
  assert( ninja == myNinja, "This function is named two things - at once!" ); 
};  //undefined
ninja(); //true
//myNinja(); //ReferenceError: myNinja is not defined
assert( typeof myNinja == "undefined", "But myNinja isn't defined outside of the function." ); //true
log( ninja );//console.log(typeof(ninja)): functioin
//typeof myNinja == 'undefined'

可以將一個匿名函式做為一個物件的屬性
以var fn= function doSth(){}這種形式定義的函式,在全域性中只能以fn為名來呼叫。

#12: We can even do it if we’re an anonymous function that’s an object property.

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; //ninja.yell(4) == 'hiyaaaa'
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." );

可以將一個物件的屬性繼承給另一個物件。

#13: But what happens when we remove the original object?

#13: But what happens when we remove the original object?

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; //ninja.yell(4) == 'hiyaaaa'
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 
 
var samurai = { yell: ninja.yell }; 
var ninja = null; //!! compared with #14
 
try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); //AssertionError [ERR_ASSERTION]
}

如果原始物件為空,則不能發生繼承。
匿名函式作為對屬性時,不會被宣告,只在被呼叫時執行。因而當對應屬性被刪除時,再呼叫該函式則無效。

#14: Let’s give the anonymous function a name!

#14: Let’s give the anonymous function a name!

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 
 
var samurai = { yell: ninja.yell }; 
var ninja = {}; //!!!!! not null
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." ); //samurai.yell(4) == 'hiyaaaa'

當屬性中的函式具名時,在屬性被建立並賦值時,便聲明瞭以屬性鍵名為名的函式。及時屬性之後被清空,函式依然存在。

#15: What if we don’t want to give the function a name?

var ninja = { 
  yell: function(n){ 
    return n > 0 ? arguments.callee(n-1) + "a" : "hiy"; 
  } 
}; //ninja.yell(4) == 'hiyaaaa'
assert( ninja.yell(4) == "hiyaaaa", "arguments.callee is the function itself." );

arguments和陣列類似但不是陣列、它儲存著函式呼叫時傳遞過來的所有引數、下標從0開始順序接收。arguments物件不是一個Array、它類似於Array,但不具有除了length方法的任何方法,例如 sort方法、pop方法等 ,但它可以被轉換為一個真正的Array。
callee是arguments物件的一個屬性、用來指向當前執行的函式。
在匿名函式中,可以用arguments.callee呼叫函式自身

#17: How similar are functions and objects?

var obj = {}; 
var fn = function(){}; 
assert( obj && fn, "Both the object and function exist." );

Function
函式就是物件,代表函式的物件就是函式物件。所有的函式物件是被 Function 這個函式物件構造出來的。也就是說,Function 是最頂層的構造器。它構造了系統中所有的物件,包括使用者自定義物件,系統內建物件,甚至包括它自已。
Object
Object 是最頂層的物件,所有的物件都將繼承 Object 的原型,你也要知道 Object 也是一個函式物件,所以說 Object 是被 Function 構造出來的。

Function 與 Object 關係圖:

var Foo= function(){}  
var f1 = new Foo();  
console.log(f1.__proto__ === Foo.prototype);  //true
console.log(Foo.prototype.constructor === Foo);  //true
var o1 =new Object();  
console.log(o1.__proto__ === Object.prototype);  //true
console.log(Object.prototype.constructor === Object); //true 
console.log(Foo.prototype.__proto__ === Object.prototype);  //true

//Function and Object  
console.log(Function.__proto__ === Function.prototype);  //true
console.log(Object.__proto__ === Function.prototype);  //true
console.log(Object.prototype.__proto__);  //null
console.log(Object.__proto__ === Function.prototype);  //true

Reference.

#18: How similar are functions and objects?

var obj = {}; 
var fn = function(){}; 
obj.prop = "some value"; 
fn.prop = "some value";  //obj.prop == fn.prop, true
assert( obj.prop == fn.prop, "Both are objects, both have the property." );

#19: Is it possible to cache the return results from a function?

function getElements( name ) { 
  var results; 
 
  if ( getElements.cache[name] ) { 
    results = getElements.cache[name]; 
  } else { 
    results = document.getElementsByTagName(name); 
    getElements.cache[name] = results; 
  } 
 
  return results; 
} 
getElements.cache = {}; 
 
log( "Elements found: ", getElements("pre").length );
log( "Cache found: ", getElements.cache.pre.length );
//Elements found:  0
//Cache found:  0

#20: QUIZ: Can you cache the results of this function?

function isPrime( num ) { 
  var prime = num != 1; // Everything but 1 can be prime 
  for ( var i = 2; i < num; i++ ) { 
    if ( num % i == 0 ) { 
      prime = false; 
      break; 
    } 
  } 
  return prime; 
} 
 
assert( isPrime(5), "Make sure the function works, 5 is prime." ); 
assert( isPrime.cache[5], "Is the answer cached?" );
//isPrime(5) true;
//isPrime.cache[5] TypeError: Cannot read property '5' of undefined

#21: One possible way to cache the results:

function isPrime( num ) { 
  if ( isPrime.cache[ num ] != null ) 
    return isPrime.cache[ num ];
     //If the parm cached,return corresponding result
   
  var prime = num != 1; // Everything but 1 can be prime 
  for ( var i = 2; i < num; i++ ) { 
    if ( num % i == 0 ) { 
      prime = false; 
      break; 
    } 
  } 
  
  isPrime.cache[ num ] = prime //cached the parm&result
  
  return prime; 
} 
 
isPrime.cache = {}; 
 
assert( isPrime(5), "Make sure the function works, 5 is prime." ); 
assert( isPrime.cache[5], "Make sure the answer is cached." );
//isPrime(5) true;
//isPrime.cache[5] true

函式作為一個物件,可以在其子物件中進行資料快取。

#23: What happens if a function is an object property?

#23: What happens if a function is an object property?

var katana = { 
  isSharp: true, 
  use: function(){ 
    this.isSharp = !this.isSharp; 
  } 
}; 
katana.use(); 
assert( !katana.isSharp, "Verify the value of isSharp has been changed." )