JavaScript學習筆記(一)//更新中
JavaScript學習筆記 - 題目練習
- Learning Advanced JavaScript
- #2: Goal: To be able to understand this function:
- #3: Some helper methods that we have:
- #5: What ways can we define functions?
- #6: Does the order of function definition matter?
- #7: Where can assignments be accessed?
- #8: Can functions be defined below return statements?
- #10: We can refer to a function, within itself, by its name.
- #11: What is the name of a function?
- #12: We can even do it if we're an anonymous function that's an object property.
- #13: But what happens when we remove the original object?
- #14: Let's give the anonymous function a name!
- #15: What if we don't want to give the function a name?
- #17: How similar are functions and objects?
- #18: How similar are functions and objects?
- #19: Is it possible to cache the return results from a function?
- #20: QUIZ: Can you cache the results of this function?
- #21: One possible way to cache the results:
- #23: What happens if a function is an object property?
- #24: What exactly does context represent?
- #25: How can we change the context of a function?
- #26: Different ways of changing the context:
- #27: QUIZ: How can we implement looping with a callback?
- #28: A possible solution for function looping:
- #30: What does the new operator do?
- #31: We have a 'this' context that is a Ninja object.
- #32: QUIZ: Add a method that gives a name to the ninja.
- #33: Add a new property and method to the object.
- #34: What happens when we forget to use the new operator?
- #35: What happens when we forget to use the new operator? (cont.)
- #36: We need to make sure that the new operator is always used.
- #37: QUIZ: Is there another, more generic, way of doing this?
- #38: A solution using arguments.callee.
- #40: Using a variable number of arguments to our advantage.
- #41: How can we find the Min/Max number in an array?
- #42: Another possible solution:
- #43: Uh oh, what's going wrong here?
- #44: QUIZ: We must convert array-like objects into actual arrays. Can any built-in methods help?
- #45: We can use built-in methods to our advantage.
- #46: QUIZ: Implement a multiplication function (first argument by largest number).
- #47: We can use call and apply to build a solution.
- #49: A basic closure.
- #50: But why doesn't this work?
- #51: Closures are frequently used for callbacks.
- #52: They're also useful for timers.
- #53: and they're also frequently used when attaching event listeners.
- #54: Private properties, using closures.
- #55: QUIZ: What are the values of the variables?
- #56: The last one is quite tricky, we'll revisit it.
- #58: Self-executing, temporary, function
- #59: Now we can handle closures and looping.
- #60: The anonymous wrapper functions are also useful for wrapping libraries.
- #61: Another way to wrap a library:
- #62: QUIZ: Fix the broken closures in this loop!
- #63: A quick wrapper function will do the trick.
- #65: Adding a prototyped method to a function.
- #66: Properties added in the constructor (or later) override prototyped properties.
- #67: Prototyped properties affect all objects of the same constructor, simultaneously, even if they already exist.
- #68: QUIZ: Make a chainable Ninja method.
- #69: The chainable method must return this.
- #71: Examining the basics of an object.
- #72: We can still use the constructor to build other instances.
- #73: QUIZ: Make another instance of a Ninja.
- #74: QUIZ: Use the .constructor property to dig in.
- #76: The basics of how prototypal inheritance works.
- #77: QUIZ: Let's try our hand at inheritance.
- #78: The result is rather straight-forward.
- #80: We can also modify built-in object prototypes.
- #81: Beware: Extending prototypes can be dangerous.
- #83: What happens when we try to bind an object's method to a click handler?
- #84: We need to keep its context as the original object.
- #85: Add a method to all functions to allow context enforcement.
- #86: Our final target (the .bind method from Prototype.js).
- #88: How does a function's length property work?
- #89: We can use it to implement method overloading.
- #90: How method overloading might work, using the function length property.
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
判斷值是否為真值有以下兩個斷言測試函式
- assert(value[, message])
這個測試函式在 【Boolean(value)】 為 【true】時通過斷言測試,否則丟擲 【AssertionError】。( value 為false,則丟擲一個帶有 message 屬性的 【AssertionError】,其中 message 屬性的值等於傳入的 message 引數的值。 如果 message 引數為 undefined,則賦予預設的錯誤資訊。) - 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
#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." )