《Learning Advanced JavaScript》学习总结

一个让我收益匪浅的教程。用这篇博文对一些关键知识点进行整理。
教程地址:https://johnresig.com/apps/learn/

1.goal

// 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))); 
  }; 
};

2.ways to define function

function fn(){}
var fn= function(){}
obj.fn = function(){}

  • order is no a matter(define promotion)
    function can be defined below return//次序不重要

  • We can refer to a function, within itself, by its name(iteration)//函数内可以调用函数自身

3.What is the name of a function?

var fn= function doSth(){}这种形式定义的函数,在全局中只能以fn为名来调用。

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

3.1 the function which is an object property can run & what about the property removed.

匿名函数作为对属性时,不会被声明,只在被调用时执行。因而当对应属性被删除时,再调用该函数则无效。

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

3.2give 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 = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );

3.3What if we don't want to give the function a name?

在匿名函数中,可以用arguments.callee调用函数自身

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

4 Functions as Objects

4.1 Is it possible to cache the return results from a function

函数作为一个对象,可以在其子对象中进行数据缓存。

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." ); 

5.Context

5.1How can we change the context of a function

改变上下文的方法

var object = {}; 
function fn(){ 
  return this; 
} 
assert( fn() == this, "The context is the global object." ); 
assert( fn.call(object) == object, "The context is changed to a specific object." );

.call()&.apply()也可以使用

5.2What does the new operator do?

new操作符做了什么
1.创建一个空对象作为this
2.this.proto指向构造函数的prototype
3.运行构造函数
4.返回this(如果函数没有return)

function Ninja(name){
  this.changeName = function(name){
    this.name = name;
  };

  this.changeName( name );
}

var ninja = new Ninja("John");
assert( ninja.name == "John", "The name has been set on initialization" );//function(name) has been implemented

ninja.changeName("Bob");

5.3 How to make sure that the new operator is always used

function User(first, last){
  if ( !(this instanceof User) )
    return new User(first, last);
  
  this.name = first + " " + last;
}

var name = "Resig";
var user = User("John", name);

assert( user, "This was defined correctly, even if it was by mistake." );
assert( name == "Resig", "The right name was maintained." );

5.4Flexible Arguments

Array-like object can implement method of Array by change context
Math.max/Math.min/sort/slice/splice

Closures

  • Closures are frequently used for callbacks/event listener/timer
  • Private properties, using closures.

Instance Type

instance.construtor

Inheritance

prototypes

Extending prototypes can be dangerous.

Object.prototype.keys = function(){
  var keys = [];
  for ( var i in this )
    keys.push( i );
  return keys;
};

var obj = { a: 1, b: 2, c: 3 };

assert( obj.keys().length == 3, "We should only have 3 properties." );

delete Object.prototype.keys;

对数组进行拓展后,for in遍历将失效,多出一项拓展函数。
因为for in 会枚举原型链上的所有属性。

Enforcing Function Context

当我们在click handler上绑定一个对象方法时。因为使用对象调用函数时,this指向该对象,所以会发生如下错误。

var Button = {
  click: function(){
    this.clicked = true;
  }
};

var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click;
document.getElementById("results").appendChild(elem);

elem.onclick();
assert( elem.clicked, "The clicked property was accidentally set on the element" );

我们需要将context指向正确的地方

function bind(context, name){
  return function(){
    return context[name].apply(context, arguments);
  };
}

var Button = {
  click: function(){
    this.clicked = true;
  }
};

var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = bind(Button, "click");
document.getElementById("results").appendChild(elem);

elem.onclick();
assert( Button.clicked, "The clicked property was correctly set on the object" );

这种绑定方法可以直接写入原型链中进行调用

Function.prototype.bind = function(object){
  var fn = this;
  return function(){
    return fn.apply(object, arguments);
  };
};//将对象方法变成一个绑定context的函数。

var Button = {
  click: function(){
    this.clicked = true;
  }
};

var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click.bind(Button);
document.getElementById("results").appendChild(elem);

elem.onclick();
assert( Button.clicked, "The clicked property was correctly set on the object" );
Function.prototype.bind = function(){
  var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();//object作为context
  return function(){
    return fn.apply(object,
      args.concat(Array.prototype.slice.call(arguments)));
  };//将fn所需的参数提前以供使用,同时保留bind时使用的参数。
};

var Button = {
  click: function(value){
    this.clicked = value;
  }
};

var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click.bind(Button, false);
document.getElementById("results").appendChild(elem);

elem.onclick();
assert( Button.clicked === false, "The clicked property was correctly set on the object" );

Function Length

方程的length等于参数的个数

Function overloading

根据参数的个数,调用不同的方法。

function addMethod(object, name, fn){
  // Save a reference to the old method
  var old = object[ name ];

  // Overwrite the method with our new one
  object[ name ] = function(){
    // Check the number of incoming arguments,
    // compared to our overloaded function
    if ( fn.length == arguments.length )
      // If there was a match, run the function
      return fn.apply( this, arguments );

    // Otherwise, fallback to the old method
    else if ( typeof old === "function" )
      return old.apply( this, arguments );
  };
}

这个策略中,新方法存储在封包函数中,旧方法作为封包的环境。封包函数根据参数个数来决定调用新旧、方法。类似的封包函数作为旧方法不断被调用,直至参数个数符合后,执行对应方法。

你可能感兴趣的:(《Learning Advanced JavaScript》学习总结)