関数プロトタイプチェーンを呼び出し、バインド方法はJavaScriptで非常に重要な概念である、密接にこのキーワードに関連して、適用され、それらの人々の理解のかなりの部分は非常に簡単かつ明白である、いわゆるjsの強固な基盤、これらの共通基盤のAPIを中心に開かれていません、この時間は、のは、徹底的な把握にそれらを見てみましょう!
ディレクトリ
- 呼び出して、適用する、基本的な導入をバインドします
- バインドコア哲学/適用/呼び出し:メソッドを借ります
- 呼び出し、アプリケーションシナリオを適用
- バインドアプリケーションシナリオ
- シニア顔質問:手書きコール/適用され、バインド
呼び出して、適用する、基本的な導入をバインドします
構文:
fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)
戻り値:
:適用/呼び出してfun
実行した結果
を返します:バインドをfun
コピーして、指定したthis
値と初期パラメータを
パラメータ
thisArg
(オプション):
fun
this
ポイントthisArg
オブジェクト- 非strictモード:ウィンドウオブジェクトにこのポイントではヌル、未定義、楽しみとして指定thisArg。
- strictモード:IS
fun
this
undefined
- 元の値(数値、文字列、ブール値)の値は、このような文字列、数値、ブール値として、オブジェクトの自動包装の元の値を指します
param1,param2
(オプション):渡すfun
パラメータを。
- あなたが通過しないか、paramは未定義/ nullの場合、それは任意のパラメータを渡す必要がないことを意味します。
- 適用二番目のパラメータは、アレイは、アレイに渡されている
fun
パラメータ。
コールcall
/ apply
/ bind
関数でなければなりません
呼び出して、適用し、三つの方法は、Functionオブジェクトの上にぶら下がっている結合し、これらのメソッドは、唯一の機能を持っています。
限り、あなたは、たとえば、機能するよう:Object.prototype.toString
それは関数であり、私たちはしばしば、このような使用方法を参照してください。Object.prototype.toString.call(data)
役割:
それが行わ上の機能の実行を変更するには、この時点では、それらについてのすべての現在の使用は、それがベースにしています。
どのようにしないコールを混同してaaplyします
このように、以下の方法を覚えて、この問題を過小評価していない、これらの2つのAPI少数派を混乱させる。
apply
でa
、それが渡され、最初fun
のパラメータがあるArray
こともa
で始まります。
違い:
コールと適用の唯一の違い
パスはfun
異なる文言パラメータ:
apply
それは配列である2番目のパラメータである:パスfun
パラメータがアレイに書き込まれます。call
2から〜n個のパラメータが渡されfun
ます。
/適用し、バインドの違いを呼び出します
実行:
- コールの後/この即時の文脈変更する機能を適用する機能の実行を
- バインド機能は、コンテキストが変更された後、返され、それが機能を実行しません。
戻り値:
- /戻るには適用さ呼び出す
fun
実行結果を - バインドが楽しいのパラメータを保存、楽しみのコピーを返し、この点の楽しさを指定します。
この例の戻り値は、バインドアプリケーション以下に詳細を解決します。
バインドコア哲学/適用/呼び出し:メソッドを借ります
我々は偉大な参照例:
ライフ:
私は通常、週末を調理する時間がない、子どもたちに煮漬けベネディクト新鮮な味を与えたかったです。しかし、もし適切な鍋は、と私は買いに来ません。だから私は、それは目的を達成する、と鍋を借りて隣人を尋ね、だけでなく、お金を節約し、両方を行います。
プログラム:
オブジェクトがメソッドを持っている、いくつかの理由で、オブジェクトBは、同じメソッドを使用する必要があり、その方法は、それを借りて、我々は一人でいるこの時間は、Bオブジェクトのメソッドを拡張し、またはそれはオブジェクトですか?
当然の方法は、目的を達成するだけでなく、メモリを節約するだけでなく、その対象が借りています。
借入方法:これは、コール/コアアイデアをバインド/適用です。
この方法は、メモリを節約し、コードの重複を減らす、この点でデータを変更するための方法によって実現されています。
呼び出し、アプリケーションのシナリオを適用します。
これらのシナリオ、彼らの哲学がある見つけることができるより多くの経験を支払う:メソッドを借ります
- データ型の分析:
Object.prototype.toString
完璧なフィットのタイプを決定するために、我々はそれがデータのほぼすべてのタイプを決定することができ借ります:
function isType(data, type) {
const typeObj = {
'[object String]': 'string',
'[object Number]': 'number',
'[object Boolean]': 'boolean',
'[object Null]': 'null',
'[object Undefined]': 'undefined',
'[object Object]': 'object',
'[object Array]': 'array',
'[object Function]': 'function',
'[object Date]': 'date', // Object.prototype.toString.call(new Date())
'[object RegExp]': 'regExp',
'[object Map]': 'map',
'[object Set]': 'set',
'[object HTMLDivElement]': 'dom', // document.querySelector('#app')
'[object WeakMap]': 'weakMap',
'[object Window]': 'window', // Object.prototype.toString.call(window)
'[object Error]': 'error', // new Error('1')
'[object Arguments]': 'arguments',
}
let name = Object.prototype.toString.call(data) // 借用Object.prototype.toString()获取数据类型
let typeName = typeObj[name] || '未知类型' // 匹配数据类型
return typeName === type // 判断该数据类型是否为传入的类型
}
console.log(
isType({}, 'object'), // true
isType([], 'array'), // true
isType(new Date(), 'object'), // false
isType(new Date(), 'date'), // true
)
- アレイベースの方法借り配列:
様々な方法のためのクラスの配列が点灯し、すべての配列タイプの真配列していないではありませんので、我々は借りる方法の配列に移動する必要があります。
例えば、メソッドの配列をプッシュ借ります。
var arrayLike = {
0: 'OB',
1: 'Koro1',
length: 2
}
Array.prototype.push.call(arrayLike, '添加元素1', '添加元素2');
console.log(arrayLike) // {"0":"OB","1":"Koro1","2":"添加元素1","3":"添加元素2","length":4}
- 最大アレイの最小値を求める適用されます。
パラメータはさらに、このような使用として、アレイ地域を広げるもメソッドを呼び出すように配列を渡す直接適用しMath.max
、Math.min
最大/最小の配列を取得します。
const arr = [15, 6, 12, 13, 16];
const max = Math.max.apply(Math, arr); // 16
const min = Math.min.apply(Math, arr); // 6
- 受け継ぎます
ES5は、親クラスのコンストラクタを借りてのメソッド/プロパティを親クラスの継承を継承しています。
// 父类
function supFather(name) {
this.name = name;
this.colors = ['red', 'blue', 'green']; // 复杂类型
}
supFather.prototype.sayName = function (age) {
console.log(this.name, 'age');
};
// 子类
function sub(name, age) {
// 借用父类的方法:修改它的this指向,赋值父类的构造函数里面方法、属性到子类上
supFather.call(this, name);
this.age = age;
}
// 重写子类的prototype,修正constructor指向
function inheritPrototype(sonFn, fatherFn) {
sonFn.prototype = Object.create(fatherFn.prototype); // 继承父类的属性以及方法
sonFn.prototype.constructor = sonFn; // 修正constructor指向到继承的那个函数上
}
inheritPrototype(sub, supFather);
sub.prototype.sayAge = function () {
console.log(this.age, 'foo');
};
// 实例化子类,可以在实例上找到属性、方法
const instance1 = new sub("OBKoro1", 24);
const instance2 = new sub("小明", 18);
instance1.colors.push('black')
console.log(instance1) // {"name":"OBKoro1","colors":["red","blue","green","black"],"age":24}
console.log(instance2) // {"name":"小明","colors":["red","blue","green"],"age":18}
詳細には触れていない多くの同様のシナリオが、ありますが、彼らは方法を借りるという考えで重要な嘘は、言葉Duokanjibianを理解していません。
使用、適用、呼び出し?、
嘘もその違い、まったく同じ効果を適用し、呼び出します
- コールにパラメータ/シーケンス決意の数、パラメータ/不確定注文の数は、Applyを使用します。
- 読みやすさを考えてみましょう:少数のパラメータを配列にパラメータを統合するために、そして、より多くの、多数のパラメータを適用するために使用し、適用されます。
- パラメータセットは、上記の最大値/最小値を求める配列として、適用して、既に配列の場合です。
例えば、次の例のように適用され、次に使用する不定のためのパラメータ/数:
const obj = {
age: 24,
name: 'OBKoro1',
}
const obj2 = {
age: 777
}
callObj(obj, handle)
callObj(obj2, handle)
// 根据某些条件来决定要传递参数的数量、以及顺序
function callObj(thisAge, fn) {
let params = []
if (thisAge.name) {
params.push(thisAge.name)
}
if (thisAge.age) {
params.push(thisAge.age)
}
fn.apply(thisAge, params) // 数量和顺序不确定 不能使用call
}
function handle(...params) {
console.log('params', params) // do some thing
}
バインドアプリケーションのシナリオ:
1.保存関数パラメータ:
古典的な顔の質問を初めて目:
for (var i = 1; i <= 5; i++) {
setTimeout(function test() {
console.log(i) // 依次输出:6 6 6 6 6
}, i * 1000);
}
この現象の理由は、まで待つことでsetTimeout
、非同期実行i
には6となっています。
JSイベントループ機構は、学生が理解されていないについては、私はこのブログを見ることができます:Jsのイベント・サイクル(イベントループ)のメカニズムと同様に例が説明します
それでは、どのように彼を作るのですか:1,2,3,4,5、それを?
多くの方法があります。
- クロージャー、変数を保存
for (var i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function () {
console.log('闭包:', i); // 依次输出:1 2 3 4 5
}, i * 1000);
}(i));
}
ここでクロージャを作成し、各サイクルは次のようになりますi
、渡された最新の値、その後、閉鎖を保存します。
- バインド
for (var i = 1; i <= 5; i++) {
// 缓存参数
setTimeout(function (i) {
console.log('bind', i) // 依次输出:1 2 3 4 5
}.bind(null, i), i * 1000);
}
実際、ここにも閉鎖で、私たちは、バインドが閉鎖される関数を返す知っています。
これは、各、初期パラメータ、このポイントの機能を保持しi
、変更バインド閉鎖は出力が1-5、アップ保持されます。
詳細は、見て、あなたが知ってもらうことができ、以下の手書きのバインド方法があります。
let
let
声明i
も出力1-5になりますようlet
ブロックレベルのスコープなので、それぞれが新しい変数を作成しますので、setTimeout
それぞれの値が異なる読み、説明。
2.コールバック関数にこの損失の問題:
これは私のハンドル、プラグイン開発VSCodeされ、ここで、共通の問題であるwebview
だけ見つけるために、実際の通信の問題がVSCodeが間違っているAPIの初めに、遭遇したことのデバッグ多くのthis
ポイント損失の問題。
class Page {
constructor(callBack) {
this.className = 'Page'
this.MessageCallBack = callBack //
this.MessageCallBack('发给注册页面的信息') // 执行PageA的回调函数
}
}
class PageA {
constructor() {
this.className = 'PageA'
this.pageClass = new Page(this.handleMessage) // 注册页面 传递回调函数 问题在这里
}
// 与页面通信回调
handleMessage(msg) {
console.log('处理通信', this.className, msg) // 'Page' this指向错误
}
}
new PageA()
コールバック関数はthis
なぜ失われて?
コールバック関数が問題になることができないとき明らかに、ときに問題の文は発生しません。
転送コールバック関数の時には問題:
this.pageClass = new Page(this.handleMessage)
過去の転送があるのでthis.handleMessage
、関数のメモリアドレスがあり、関数が結合しないことを意味し、何のコンテキストオブジェクトが存在しないthis
点。
それはthis
、それが適用されるポイント結合ルールを:
class Page {
constructor(callBack) {
this.className = 'Page'
// callBack() // 直接执行的话 由于class 内部是严格模式,所以this 实际指向的是 undefined
this.MessageCallBack = callBack // 回调函数的this 隐式绑定到class page
this.MessageCallBack('发给注册页面的信息')
}
}
今、あなたは問題を知っていること、そして私たちだけバインドコールバック関数のthis
ポイントがあるPageA
問題を解決するために。
コールバック関数この損失ソリューション:
bind
バインディングのコールバック関数のthis
ポイント:
これは、シナリオが結合する代表的なアプリケーションであり、この点に結合する、コールバック関数として使用されます。
this.pageClass = new Page(this.handleMessage.bind(this)) // 绑定回调函数的this指向
PS:これはなぜあるの関数コールバック関数をバインドするとき、ほかのバインディングバインドを使用する点が、また同じ問題と原則ので。react
render
this
- この点をバインドする機能を矢印
この矢印ポインティング機能、この一般的な機能の第一の外側層で定義された場合、クラス分類は、本明細書に言及されます。PageA
:私はブログを書いた前に、内容のこの作品は、あなたが見ることができる詳細な検討事項との違いは、矢印の機能と機能の正常な機能、NAシーンを矢印します
this.pageClass = new Page(() => this.handleMessage()) // 箭头函数绑定this指向
シニアインタビューの質問 - 手書きコール/適用され、バインド:
インタビューのメーカーでは、手書きが(特にバインド)、バインドを適用し、通話を実現するフェイス質問の比較的高い周波数であったが、ここでは、これらの機能を達成するために見てみましょう。
あなたは手書き達成することができcall
、それを?
考え
- 規則に従って配置されたコンテキストオブジェクトを呼び出す、すなわち
this
ポイント。 - 設定することにより
context
特性を、この点はコンテキストに暗黙的結合の関数となります - 暗黙的結合およびパスパラメータによって実行される機能。
- 一時的な属性を削除し、関数が実行結果を返します。
Function.prototype.myCall = function (context, ...arr) {
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
context.testFn = this; // 函数的this指向隐式绑定到context上
let result = context.testFn(...arr); // 通过隐式绑定执行函数并传递参数
delete context.testFn; // 删除上下文对象的属性
return result; // 返回函数执行结果
};
コンテキストオブジェクト機能の分析:
多くの人々は、単にに、コンテキストオブジェクトの機能を判断するcontext
ような誤った判断、するかどうか:
// 判断函数上下文绑定到`window`不够严谨
context = context ? Object(context) : window;
context = context || window;
テストの後、次の3つの場合のように偽の、コンテキストオブジェクトはにバインドするように機能しますwindow
。
// 网上的其他绑定函数上下文对象的方案: context = context || window;
function handle(...params) {
this.test = 'handle'
console.log('params', this, ...params) // do some thing
}
handle.elseCall('') // window
handle.elseCall(0) // window
handle.elseCall(false) // window
call
コンテキストオブジェクト関数は、オブジェクト・インスタンスの元の値にバインドされます。
したがって、正解は、それは私がしなければならないもののようにする必要があります:
// 正确判断函数上下文对象
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
あなたは手書き達成することができapply
、それを?
アイデア:
- ハンドラに渡されたパラメータの他の部分と同じではありません
call
同じ。 apply
第2のパラメータは、クラスは、アレイに権威ガイドとJavaScriptはクラスオブジェクトのメソッドか否かが判定されるオブジェクトの配列を受け入れています。
Function.prototype.myApply = function (context) {
if (context === null || context === undefined) {
context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
// JavaScript权威指南判断是否为类数组对象
function isArrayLike(o) {
if (o && // o不是null、undefined等
typeof o === 'object' && // o是对象
isFinite(o.length) && // o.length是有限数值
o.length >= 0 && // o.length为非负值
o.length === Math.floor(o.length) && // o.length是整数
o.length < 4294967296) // o.length < 2^32
return true
else
return false
}
context.testFn = this; // 隐式绑定this指向到context上
const args = arguments[1]; // 获取参数数组
let result
// 处理传进来的第二个参数
if (args) {
// 是否传递第二个参数
if (!Array.isArray(args) && !isArrayLike(args)) {
throw new TypeError('myApply 第二个参数不为数组并且不为类数组对象抛出错误');
} else {
args = Array.from(args) // 转为数组
result = context.testFn(...args); // 执行函数并展开数组,传递函数参数
}
} else {
result = context.testFn(); // 执行函数
}
delete context.testFn; // 删除上下文对象的属性
return result; // 返回函数执行结果
};
あなたは手書き達成することができbind
、それを?
主な計画:
手書きのbind
理解はそれが行く提供保持するのに十分な深さを持っている、インタビューのシニアフロントエンド場合、質問の顔にメーカーの高頻度であるが、それらの間の違いを見分けることができ、使用状況や前面に来ることができません!
考え
- 関数のコピー元:
- 可変ソース機能を格納することにより
- 使用
Object.create
fToBindにコピー元の関数プロトタイプを
- コピー機能が戻ります
- 機能のコピーを呼び出します。
- 判決に新しいコール:によって
instanceof
スルー機能するかどうかを判断するには、new
結合を決定するために呼び出しますcontext
- これは、パラメータを渡すために結合します+
- ソースに戻す機能の実行結果
- 判決に新しいコール:によって
Function.prototype.myBind = function (objThis, ...params) {
const thisFn = this; // 存储源函数以及上方的params(函数参数)
let fToBind = function () {
const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上
return thisFn.apply(context, params); // 用apply调用源函数绑定this的指向并传递参数,返回执行结果
};
fToBind.prototype = Object.create(thisFn.prototype); // 复制源函数的prototype给fToBind
return fToBind; // 返回拷贝的函数
};
概要
もともと私は数日間オフ書き込み、最終的に明確に記載されているこれら3つのAPIの知識が、私はあなたがインタビューの後読みを期待して、この問題が発生した、あなたは海、空缶の結果、これはすぐに書かれるだろうと思いましたラウンド強制的にロードされました^ _ ^
私は、私にいくつかのより多く与え、あなたにブログが参考に考えてスターになりました!
高度なフロントエンドの蓄積、公共の数字、GitHubの、WX:OBkoro1、Eメール:[email protected]
2019年8月30日上記