JavaScriptの深い研究(一)
20110 ワード
1.undefinedとnull
JavaScriptで変数をundefinedまたはnullに割り当てるのは基本的に違いありません.if文ではデフォルトでfalseに変換され、==演算子を使用する場合、両者は等しい.
違い:JavaScriptを使用する場合、nullを「オブジェクトなし」と表現するのは、JAVAのオブジェクト初期化に似ており、このオブジェクトの存在はnullとして初期化されていると一般的に考えられています.数値を変換するときになります. undefinedは、なしの元の値、すなわち、このオブジェクトがなく、数値を変換するときにNaNを表す.したがって,nullはオブジェクトが存在し,undefinedはオブジェクトが存在しないことに重点を置いている.
2.callのシミュレーション実装
プロフィール:
call()メソッドは、指定したthis値といくつかの指定したパラメータ値を使用して、関数またはメソッドを呼び出します.
ここで
3.applyのシミュレーション実装
callの実装と同様に、異なるパラメータは異なり、callは任意のパラメータを受け入れることができ、applyはパラメータ配列を受け入れる必要がある.
4.bindのシミュレーション実装
bind()メソッドは新しい関数を作成します.この新しい関数が呼び出されるとbind()の最初のパラメータが実行時のthisとして使用され、その後のシーケンスパラメータが伝達された実パラメータの前にそのパラメータとして送信されます.
5.newのシミュレーション実装
new演算子は、ユーザー定義のオブジェクトタイプのインスタンスまたはコンストラクション関数を持つ組み込みオブジェクトタイプの1つを作成します.
6.配列平坦化
配列フラット化とは、1つの多次元配列を1次元配列に変更することです.
例:[1,2,3,[4,5]=>[1,2,3,4,5]
実現する
1).ループループを使用
2).toStringの使用
3).joinの使用
==`メソッド2,3には弊害がある:数字のみをサポートする==
4).es 5のreduceを使用
5).es 6拡張演算子の使用
7.ブレ止め
数回の操作を1つに統合します.原理は、delay時間後に関数をトリガするようにタイマーを維持することですが、delay時間内に再びトリガすると、前のタイマーをキャンセルして再設定します.これにより,最後の操作のみがトリガされる.
1.第1版
2.第2版
8.スロットル
スロットルの目的は,頻繁な関数やイベントを間欠的にトリガできるようにすることであり,scroll,resize,moveのようなイベントは1秒に何度もトリガされ,プログラムの性能に大きな影響を及ぼす可能性がある.我々はスロットル方式を採用して、イベントをそんなに頻繁にトリガしないようにして、私たちはスロットル関数を通じて制御して、トリガする必要があるイベントが次のイベントがトリガする前にまだトリガしていない場合、私たちは前のイベントをトリガさせないで、一定時間内に1回の関数だけをトリガします
1.第1版:
2.第2版
3.第3版(
JavaScriptで変数をundefinedまたはnullに割り当てるのは基本的に違いありません.if文ではデフォルトでfalseに変換され、==演算子を使用する場合、両者は等しい.
違い:JavaScriptを使用する場合、nullを「オブジェクトなし」と表現するのは、JAVAのオブジェクト初期化に似ており、このオブジェクトの存在はnullとして初期化されていると一般的に考えられています.数値を変換するときになります. undefinedは、なしの元の値、すなわち、このオブジェクトがなく、数値を変換するときにNaNを表す.したがって,nullはオブジェクトが存在し,undefinedはオブジェクトが存在しないことに重点を置いている.
2.callのシミュレーション実装
プロフィール:
call()メソッドは、指定したthis値といくつかの指定したパラメータ値を使用して、関数またはメソッドを呼び出します.
Function.prototype.call = function(context){
var context = context || window;
context.fn = this;
var args = [];
for(var i = 0 ; i< arguments.length;i++){
args.push('arguments['+i+']');
}
var result = eval('context.fn('+res+')');
delete conext.fn();
return result;
}
//ES6
Function.prototype.call = function(context,...args){
var context = context || window;
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
}
ここで
args.push('arguments['+i+']');
の行を簡単に分析すると、なぜargumentsが引用符を付ける必要があるのか.eval
を実行するときに入力する必要があるのは文字列であり、値ではない.引用符を付けないargsに格納されるのは入力パラメータの値であり、例えば、入力callのパラメータは[‘zhangsan’,’lis’]であり、このときのargs=[‘zhangsan’,’lis’]である.eval('context.fn('+res+')');
という行のコードを実行すると、実際には'zhangsan','lisi'
と入力され、実行が間違っています.3.applyのシミュレーション実装
callの実装と同様に、異なるパラメータは異なり、callは任意のパラメータを受け入れることができ、applyはパラメータ配列を受け入れる必要がある.
Function.prototype.apply = function(context,arr){
var context = context || window;
context.fn = this;
if(!arr){
return context.fn();
}
var args = [];
var result ;
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn('+args+')');
delete context.fn;
return result;
}
//es6
Function.prototype.apply = function(context,...arr){
var context = context || window;
context.fn = this;
let result = context.fn(...arr);
delete context.fn;
returl result;
}
4.bindのシミュレーション実装
bind()メソッドは新しい関数を作成します.この新しい関数が呼び出されるとbind()の最初のパラメータが実行時のthisとして使用され、その後のシーケンスパラメータが伝達された実パラメータの前にそのパラメータとして送信されます.
Function.prototype.bind = Function.prototype.bind || function(context){
var self = this;//
var args = Array.prototype.slice.call(arguments,1);
var fNOP = function(){};
var fBound = function(){
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context,args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
// fBound.prototype = Object.create(this.prototype);
return fBound;
}
//es6
Function.prototype.bind = function(context,...rest){
var self = this;//
return function F(...args){
if(this instanceof self){
return new self(...rest,...args);
}
return self.apply(context,rest.concat(args);
}
}
Object.create = function(o){
function f(){}
f.prototype = o;
return new f;
}
5.newのシミュレーション実装
new演算子は、ユーザー定義のオブジェクトタイプのインスタンスまたはコンストラクション関数を持つ組み込みオブジェクトタイプの1つを作成します.
function objectFactory(){
var obj = new Object(),
Constructor = [].shift.call(arguments);
obj._proto_ = Constructor.prototype;
var result= Constructor.apply(obj,arguments);
return typeof result=== 'object' ? result: obj;
}
6.配列平坦化
配列フラット化とは、1つの多次元配列を1次元配列に変更することです.
例:[1,2,3,[4,5]=>[1,2,3,4,5]
実現する
1).ループループを使用
function flatten(arr){
var result = [];
for(var i=0;iif(Array.isArray(arr[i])){
result = result.concat(flatten(arr[i]))
}else{
result.push(arr[i]);
}
}
return result;
}
2).toStringの使用
function flatten_tostring(arr){
return arr.toString().split(',').map(function(item){
return +item;
});
}
3).joinの使用
function flatten_join(arr){
return arr.join(",").split(",").map(function(item)){
return +item;
}
}
==`メソッド2,3には弊害がある:数字のみをサポートする==
4).es 5のreduceを使用
function flatten_reduce(arr){
return arr.reduce(function(prev,next){
return prev.concat(Array.isArray(next)?flatten_reduce(next) : next);
},[])
}
5).es 6拡張演算子の使用
function flatten_es6(arr){
while(arr.some((item)=>Array.isArray(item))){
arr = [].concat(...arr);
}
return arr;
}
7.ブレ止め
数回の操作を1つに統合します.原理は、delay時間後に関数をトリガするようにタイマーを維持することですが、delay時間内に再びトリガすると、前のタイマーをキャンセルして再設定します.これにより,最後の操作のみがトリガされる.
1.第1版
function _debounce(fn,delay){
var delay = delay || 200;
var timer;
return function(){
var context = this,
args = arguments;
if(timer){
clearTimeout(timer);
}
timer = setTimeout(function(){
timer = null;
fn.apply(context,args);
},delay);
}
}
2.第2版
function _debounce(fn,wait,immedilate){
var timer,
args,
context,
timerstap,
result;
var later =function(){
var last = new Date().getTime() -timestamp;
if(last=0){
timer = setTimeout(later,wait-last);
}else{
timer = null;
if(!immedilate){
result = fn.apply(context,args);
if(!timer){
context =args = null;
}
}
}
};
return function(){
context = this;
args = arguments;
timerstap = new Date().getTime();
var callNow = immedilate && !timer;
if(!timer){
timer = setTimeout(later,wait);
}
if(callNow){
result = fn.apply(context,args);
context = args = null;
}
return result;
}
}
8.スロットル
スロットルの目的は,頻繁な関数やイベントを間欠的にトリガできるようにすることであり,scroll,resize,moveのようなイベントは1秒に何度もトリガされ,プログラムの性能に大きな影響を及ぼす可能性がある.我々はスロットル方式を採用して、イベントをそんなに頻繁にトリガしないようにして、私たちはスロットル関数を通じて制御して、トリガする必要があるイベントが次のイベントがトリガする前にまだトリガしていない場合、私たちは前のイベントをトリガさせないで、一定時間内に1回の関数だけをトリガします
1.第1版:
var throttle =function(fn,interval){
var _self = fn,
timer,
firstTime = true;
return function(){
var args = arguments,
_this = this;
if(firstTime){
_self.apply(_this,args);
return firstTime =false;
}
if(timer){
firstTime = false;
}
timer = setTimeout(function(){
clearTimeout(timer);
timer = null;
_self.apply(_this,args);
},interval||500)
}
}
2.第2版
var throttle = function(fn,interval){
var last,timer,interval = interval||500;
return function(){
var context = this,
args = arguments,
now += new Date();
if(last&&now-lastfunction(){
last = now;
fn.apply(context,args);
},inerval)
}else{
last = now;
fn.apply(context,args);
}
}
}
3.第3版(
underscore
より) var throttle =function(fn,wait,options){
var context,
args,
timer =null,
result,
previous =0;
if(!options){
options ={};
}
var later =function(){
pervious = options.leading == false ? 0 : new Date();
timer = null;
result = fn.apply(context,args);
if(!timer){
context = args = null;
}
};
return function(){
var now = new Date();
if(!pervious && options.leading == false){
pervious = now;
}
var remaning = wait - (now - pervious);
context = this;
args = arguments;
if(remaning<=0||remaning>wait){
if(timer){
clearTimeout(timer);
timer = null;
}
previous = now;
result = fn.apply(context,args);
if(!timer){
context = args = null;
}else if(!timer && options.trailing != false){
timer =setTimeout(later,remaning);
}
return result;
}
}
}