JavaScript のこのポインターについての深い理解

小云云
リリース: 2018-03-17 16:21:10
オリジナル
1574 人が閲覧しました

この記事では主に、JavaScript の this ポインターについて詳しく説明します。Java を作成する際、これが誤って使用されると、idea は直接エラーを報告します。

たとえば...



オブジェクト指向プログラミングには 2 つの重要な概念があります: 1 つはクラスで、もう 1 つはインスタンス化されたオブジェクトです クラスは抽象概念です。メタファーの観点から言えば、クラスは型のようなものであり、インスタンス化されたオブジェクトは、その型を通して製造された製品です。ただし、クラスとインスタンス化されたオブジェクトの間には密接な関係があります。金型と金型で作られた製品の関係と同様に、クラスの機能はインスタンス化されたオブジェクトを置き換えることはできません。その用途は異なります。

上記のコードから、this ポインタは Java 言語のインスタンス化されたオブジェクトでのみ使用できることがわかり、このポインタはインスタンス化されたオブジェクトと等しく、この後にドット演算子が追加されます。物とは、名前、仕事、手、足など、これが所有する物です。

実際 JavaScript の this ポインターの論理概念もインスタンス化されたオブジェクトです。ただし、JavaScript の this ポインターは Java の this ポインターよりも理解するのがはるかに困難です。結局のところ、私は個人的に次の 3 つの基本的な理由があると考えています:

理由 1: JavaScript は関数型プログラミング言語です。奇妙なことに、この関数型プログラミング言語がオブジェクト指向言語でもあることを示すポインターもあります。具体的には、JavaScriptでは高階関数をオブジェクトとして渡すことができると同時に、このコンストラクターもJavaScriptで使用することができます。インスタンス化されたオブジェクトを作成すると、メソッドの実行時に this ポインターが生成され、方向は常に変化するため、制御が困難になります。

理由 2: JavaScript のグローバル スコープは this ポインターに大きな影響を与えます。上記の Java の例から、this ポインターは new 演算子を使用した後にのみ有効になることがわかりますが、JavaScript の this はそれです。現時点では、新しい操作が実行されない場合でも有効になります。多くの場合、これはグローバル オブジェクト ウィンドウを指します。

理由 3: JavaScript の call 演算子と apply 演算子は this ポインタを自由に変更できます。これは非常に柔軟に見えますが、この不合理なアプローチはこのポインタの本来の理解の意味を破壊し、また、作成時にこれを理解することを困難にします。



の本当のポイントは、このポインターを使用する従来の方法に違反しているためです。実際の開発では、これら 3 つの理由はすべて異なります。それらは絡み合う傾向があり、これはすべて雲の中です...


入門書: Web 開発者のための専門的な Javascript、- 上級バージョンは次のとおりです:

これは常にポイントしますto このメソッドが呼び出されるオブジェクトです。



var name="zhoulujun";

function Say(){

console.log(this.name)

}

say(); //zhoulujun



このポインタ (ウィンドウ オブジェクトを指す) は、次の場合でも直接使用できます 。三重等号が使用されている場合、それらは等しいです。グローバル スコープは、JavaScript 言語の特性の理解を妨げることがよくあります。この干渉の本質は次のとおりです。

JavaScript 言語では、グローバル スコープはウィンドウではなくオブジェクトとして理解される

。つまり、ウィンドウはインスタンス化されたオブジェクトです。プログラマーが制御できないため、ページ全体の要素は JavaScript エンジンによって完了します。プログラミング言語を通じてインスタンス化プロセスを操作するため、開発中にこのポインターを構築する感覚がなくなり、このポインターがコード内でウィンドウを指す状況を理解するのが妨げられることがよくあります。

ここでは、これは window オブジェクトを指しているので、this.name->zhoulujun!



say 関数が実行されると、JavaScript は Execute コンテキスト (実行コンテキスト) を作成します。実行コンテキストには、say 関数の実行時に必要なすべての情報が含まれます。実行コンテキストには独自のスコープ チェーンもあります。関数が実行されると、JavaScript エンジンはまず、say 関数を使用してスコープ チェーンから実行コンテキストのスコープ チェーンを初期化します。

ここで大まかに覚えておいてください:

var myObj={
name:"zhoulujun",
fn:function(){
console.log(this.name)
}
};
myObj.fn();
ログイン後にコピー


这里的this指向obj,因为fn()运行在obj里面……

然后再来看……

var name="zhoulujun";
function say(){
console.log(this.name)
console.log(this)
}
say();
function say2(){
var site="zhoulujun.cn";
console.log(this.site);
}
say2();
ログイン後にコピー


myObj2={
site:"zhoulujun.cn",
fn:function(){
console.log(this.site)
}
}
ログイン後にコピー



这里的this指向的是对象myObj2,因为你调用这个fn是通过myObj2.fn()执行的,那自然指向就是对象myObj2,这里再次强调一点,this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个

然后,我们更深入(受不了 …………

myObj3={
site:"zhoulujun.cn",
andy:{
site:"www.zhoulujun.cn",
fn:function(){
console.log(this.site)
}
}
};
myObj3.andy.fn();
ログイン後にコピー



这里同样也是对象Object点出来的,但是同样this并没有执行它,那你肯定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解this的指向的问题。

如果,你实在理解不了,就这么样背下来吧!

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,如果不相信,那么接下来我们继续看几个例子。

这样既对了吗??深入点(就受不了了……讨厌……

myObj3={
site:"zhoulujun.cn",
andy:{
site:"www.zhoulujun.cn",
fn:function(){
console.log(this)
console.log(this.site)
}
}
};
//    myObj3.andy.fn();
var fn=myObj3.andy.fn;
fn();
ログイン後にコピー



其实,这里的 fn等价于

fn:function(age){

console.log(this.name+age);

}

下面我们来聊聊函数的定义方式:声明函数和函数表达式

我们最上面第一个案例定义函数的方式在javascript语言称作声明函数,第二种定义函数的方式叫做函数表达式,这两种方式我们通常认为是等价的,但是它们其实是有区别的,而这个区别常常会让我们混淆this指针的使用,我们再看看下面的代码:



say は実行できるのに、say3 は実行できないのはなぜですか?その理由は、



say3の出力結果が未定義である理由は、前回の記事で述べました未定義というのは、メモリのスタック領域に既に変数名が存在するという意味ですが、スタック領域に変数値がありません。同時に、ヒープ領域にも特定のオブジェクトがありません。これは、JavaScript エンジンがプリロード中に変数定義をスキャンすることが原因です。ただし、ftn01 の出力結果は非常に驚くべきものです。完成した関数定義が出力され、コードが順番に実行されない、これは 1 つの問題しか説明できません:

JavaScript 言語では、関数は関数を宣言することによって定義され、JavaScript エンジンは前処理中に関数の定義と割り当て操作を完了します。ここで、JavaScript における前処理の特徴を追加します。前処理は実行環境に関係します。実行環境には、グローバル実行環境とローカル実行環境の 2 つのカテゴリがあると述べました。実行環境はコンテキスト変数を通じて反映されます。実際、このプロセスは関数が実行される前に完了します。つまり、前処理と実行環境の構築の主な目的は、変数の定義を明確にすることです。ただし、グローバル スコープの構築中またはグローバル変数の前処理中の関数の宣言では、変数の定義と代入の操作が同時に完了するため、上記のコードの結果がわかります。 。宣言された関数はグローバル スコープの構築中に完成するため、宣言された関数はすべて window オブジェクトの属性になります。これは、関数をどこで宣言しても、宣言された関数が最終的に window オブジェクトに属する理由を説明します。

ここで見ることをお勧めします - Java クラスの実行順序:

http://www.zhoulujun.cn/zhoulujun/html/java/javaBase/7704.html



其实在javascript语言里任何匿名函数都是属于window对象,它们也都是在全局作用域构造时候完成定义和赋值,但是匿名函数是没有名字的函数变量,但是在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象,所以上面代码执行时候fn的this都是指向window,因为javascript变量名称不管在那个作用域有效,堆区的存储的函数都是在全局执行环境时候就被固定下来了,变量的名字只是一个指代而已。

类似的情况(面试题喜欢这么考!)……比如:



this都是指向实例化对象,前面讲到那么多情况this都指向window,就是因为这些时候只做了一次实例化操作,而这个实例化都是在实例化window对象,所以this都是指向window。我们要把this从window变成别的对象,就得要让function被实例化,那如何让javascript的function实例化呢?答案就是使用new操作符。

再来看 构造函数:

function  User(){
this.name="zhoulujun";
console.log(this);
}
var andy=new User();
console.log(andy.name)
ログイン後にコピー



why andy 的name 是 zhoulujun,那是:因为:

new关键字可以改变this的指向,将这个this指向对象andy,

那andy什么时候又成了思密达,oh,no,is Object?

new キーワードを使用すると、オブジェクト インスタンスが作成されるためです (重要なことを 3 回静かに読んでください)

変数 andy を使用して、User ユーザー インスタンスを作成します (User のコピーをオブジェクト andy にコピーするのと同じです)。これは作成されただけで実行されていません。この関数 User を呼び出すオブジェクトはオブジェクト andy なので、オブジェクト User に名前があるのはなぜでしょうか。オブジェクト andy に対して new キーワードを使用することは、コピーを作成することと同じです。

java プログラマ: Class user=new User(); は見覚えのあるものですね...



function

が関数である場合、それは関数でもあり、オブジェクトでもあります。 javascriptのコンストラクターは、クラスとコンストラクターを1つに結合したものだとよく思いますもちろん、javascriptの言語仕様にはクラスの概念はありませんが、私の理解では次のようになります。コンストラクターと通常の関数として使用される関数の違いは、こうすることで理解しやすくなります 以下に、「JavaScript 高度なプログラミング」の新しい演算子の説明を掲載します。 新しい演算子は、コンストラクターに次の変更を加えます。

1. 新しいオブジェクトを作成します。コンストラクターのスコープが新しいオブジェクトに割り当てられます (つまり、これは新しいオブジェクトを指します)

3. コンストラクターでコードを実行します (この新しいオブジェクトに属性を追加します)。

4. 返回新对象

……

妈的:读的那么拗口,不明觉厉…………看图……还不

不明白……

var myObj5={
name:"andy"
};
var myObj6=new Object();
myObj6.name="andy";
function  say5(name){
console.log(name)
}
var say6=new Function("name","console.log(name)");
console.log(myObj5)
console.log(myObj6)
say5("andy");
say6("andy");
ログイン後にコピー



还不明白,就请奶奶买块豆腐,撞死算了……

第四点也要着重讲下,记住构造函数被new操作,要让new正常作用最好不能在构造函数里写return,没有return的构造函数都是按上面四点执行,有了return情况就复杂了

return这王八蛋……



那么我这样呢……



does it have to be like this?Tell me why(why),is there something I have missed?

Tell me why(why),cos I don't understand…………



那是因为……because of u?no return……



所以:如果返回的是基本类型,就会丢掉…只能返回Object类型……typeof xx ===“object”

看到called 没有?什么鬼!!

其实new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

var a={
name:"andy",
site:"zhoulujun.cn",
fn:function(age){
console.log(this.name+age);
}
};
var b={
name:"zhoulujun",
site:"www.zhoulujun.cn",
fn:function(age){
console.log(this.name+age);
}
};
a.fn(2); //andy2
a.fn.call(b,2) //zhoulujun2
a.fn.apply(b,[2])//zhoulujun2
当然,还有bind……
var arr = [1, 2];
var add = Array.prototype.push.bind(arr, 3);
// effectively the same as arr.push(3)
add();
// effectively the same as arr.push(3, 4)
add(4);
console.log(arr);
// <- [1, 2, 3, 3, 4]
在下面的例子,this将无法在作用域链中保持不变。这是规则的缺陷,并且常常会给业余开发者带来困惑。
function scoping () {
console.log(this);
return function () {
console.log(this);
};
}
scoping()();
// <- Window
// <- Window
有一个常见的方法,创建一个局部变量保持对this的引用,并且在子作用域中不能有同命变量。子作用域中的同名变量将覆盖父作用域中对this的引用。
function retaining () {
var self = this;
return function () {
console.log(self);
};
}
retaining()();
// <- Window
除非你真的想同时使用父作用域的this,以及当前this值,由于某些莫名其妙的原因,我更喜欢是使用的方法.bind函数。这可以用来将父作用域的this指定给子作用域。
function bound () {
return function () {
console.log(this);
}.bind(this);
}
bound()();
// <- Window
ログイン後にコピー

写到这里,都看不下去,逻辑有点混乱,有的是从前辈哪里引用的。

改天有时间整理下,然后,在去讲下闭包(……closer





以上がJavaScript のこのポインターについての深い理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート