Maison > interface Web > js tutoriel > le corps du texte

Tout ce que vous voulez savoir sur les astuces JavaScript scopes_javascript

WBOY
Libérer: 2016-05-16 15:16:12
original
1163 Les gens l'ont consulté

Il existe une série de concepts de portée en Javascript. Les développeurs qui découvrent JS ne peuvent pas comprendre ces concepts, et même certains développeurs expérimentés peuvent ne pas y parvenir. L'objectif principal de cet article est d'aider à comprendre certains concepts en JavaScript tels que : portée, fermeture, ceci, espace de noms, portée de fonction, portée globale, portée lexicale et portée publique/privée. J'espère que cet article pourra répondre aux questions suivantes : <.>

  • Qu'est-ce que la portée ?
  • Que sont les périmètres global et local ?
  • Quelle est la différence entre l'espace de noms et la portée ?
  • Quel est ce mot-clé et comment la portée l'affecte-t-elle ?
  • Qu'est-ce que la portée fonctionnelle et la portée lexicale ?
  • Qu'est-ce qu'une fermeture ?
  • Que sont les périmètres publics et privés ?
  • Comment comprendre et créer le contenu ci-dessus ?

1. Qu'est-ce que la portée ? En JavaScript, la portée fait généralement référence au contexte du code. Possibilité de définir une portée globale ou locale. Comprendre la portée de JavaScript est une condition préalable pour écrire du code robuste et devenir un bon développeur. Vous devez savoir où trouver les variables et les fonctions, où modifier la portée du contexte de votre code et comment écrire un code rapide, lisible et facile à déboguer.

Imaginer le scope est très simple, sommes-nous dans le scope A ou dans le scope B ?

2. Qu'est-ce que la portée mondiale ? Avant d’écrire la première ligne de code JavaScript, nous sommes dans le cadre global. À ce stade, nous définissons une variable, généralement une variable globale.

// global scopevar name = 'Todd';
Copier après la connexion
La portée mondiale est à la fois votre amie et votre cauchemar. Apprendre à contrôler la portée est facile, et une fois que vous aurez appris à utiliser les variables globales, vous ne rencontrerez plus de problèmes (généralement des conflits d'espace de noms). J’entends souvent les gens dire que « la portée mondiale est mauvaise », sans jamais réfléchir sérieusement aux raisons. Ce n’est pas que la portée globale soit mauvaise, c’est un problème d’utilisation. Lors de la création de modules/API multi-portées, nous devons les utiliser sans causer de problèmes.

jQuery('.myClass');
Copier après la connexion
...Nous obtenons jQuery dans la portée globale, nous pouvons appeler cette référence un espace de noms. Un espace de noms fait généralement référence à une portée dans laquelle des mots peuvent être échangés, mais qui fait généralement référence à une portée de niveau supérieur. Dans l'exemple ci-dessus, jQuery se trouve dans la portée globale, également appelée espace de noms. jQuery est défini dans la portée globale en tant qu'espace de noms, qui sert d'espace de commande de la bibliothèque jQuery. Tout le contenu de la bibliothèque devient les descendants de l'espace de noms.

2. Qu'est-ce que la portée locale ? La portée locale vient généralement après la portée globale. De manière générale, il existe une portée globale et chaque fonction définit sa propre portée locale. Toute fonction définie à l'intérieur d'une autre fonction a une portée locale liée à la fonction externe.
Si vous définissez une fonction et créez des variables à l'intérieur, ces variables sont des variables locales. Par exemple :

// Scope A: Global scope out here
var myFunction = function () {
// Scope B: Local scope in here};
Copier après la connexion
Les variables locales ne sont pas visibles par les variables globales. Sauf exposition au monde extérieur. Si des fonctions et des variables sont définies dans une nouvelle portée, ce sont des variables dans la nouvelle portée actuelle et ne sont pas accessibles en dehors de la portée actuelle. Voici un exemple simple :

var myFunction = function () {
var name = 'Todd';
console.log(name); // Todd};
// Uncaught ReferenceError: name is not defined
console.log(name);
Copier après la connexion
Le nom de la variable est une variable locale et n'est pas exposé à la portée parent, il n'est donc pas défini.

3. Portée de la fonction La portée de la fonction en JavaScript est la portée minimale. Ni les boucles for ni while, ni if ​​et switch ne peuvent créer de portées. La règle est que les nouvelles fonctions ont de nouveaux domaines. Un exemple simple de création d'un domaine est le suivant :

// Scope A
var myFunction = function () {
// Scope B
var myOtherFunction = function () {// Scope C};};
Copier après la connexion
Il est très pratique de créer de nouvelles variables, fonctions et objets de domaine et locaux.

4. Portée lexicale Lorsqu'une fonction est imbriquée dans une autre fonction et que la fonction interne peut accéder à la portée de la fonction externe, cette méthode est appelée portée lexicale (Lexical Socpe) ou fermeture, également connue sous le nom de portée statique. L'exemple qui illustre le mieux ce problème est le suivant :

// Scope A
var myFunction = function () {
// Scope B
var name = 'Todd'; // defined in Scope B
var myOtherFunction = function () {
// Scope C: `name` is accessible here!};
};
Copier après la connexion
MyOtherFunction est simplement défini ici et non appelé. Cet ordre des appels affecte également la sortie des variables. Ici, je définis et appelle une fonction dans une autre console.

var myFunction = function () {
var name = 'Todd';
var myOtherFunction = function () {
console.log('My name is ' + name);
};
console.log(name);
myOtherFunction(); // call function
};
// Will then log out:// `Todd`
// `My name is Todd`
Copier après la connexion
La portée lexicale est plus pratique à utiliser. Toutes les variables, objets et fonctions définis dans la portée parent peuvent être utilisés dans sa chaîne de portée de domaine. Par exemple :

var name = 'Todd';
var scope1 = function () {
// name is available here
var scope2 = function () {// name is available here too
var scope3 = function () {// name is also available here!};
};
};
Copier après la connexion

唯一需要注意的事情是词汇域不后项起作用,下面的方式词汇域是不起作用的:

// name = undefined
var scope1 = function () {
// name = undefined
var scope2 = function () {// name = undefined
var scope3 = function () {var name = 'Todd'; // locally scoped};
};
};
Copier après la connexion

能返回对name的引用,但是永远也无法返回变量本身。

5、作用域链
函数的作用域由作用域链构成。我们知道,每个函数可以定义嵌套的作用域,任何内嵌函数都有一个局部作用域连接外部函数。这种嵌套关系我们可以称为链。域一般由代码中的位置决定。当解释(resolving)一个变量,通常从作用域链的最里层开始,向外搜索,直到发现要寻找的变量、对象或者函数。

6、闭包(Closures)
闭包和词法域( Lexical Scope)很像。返回函数引用,这种实际应用,是一个可以用来解释闭包工作原理的好例子。在我们的域内部,我们可以返回对象,能够被父域使用。

var sayHello = function (name) {
var text = 'Hello, ' + name;
return function () {
console.log(text);};
};
Copier après la connexion

这里我们使用的闭包,使得我们的sayHello内部域无法被公共域访问到。单独调用函数并不作任何操作,因为其单纯的返回一个函数。

sayHello('Todd'); // nothing happens, no errors, just silence...
Copier après la connexion

函数返回一个函数,也就意味着需要先赋值再调用:

var helloTodd = sayHello('Todd');
helloTodd(); // will call the closure and log 'Hello, Todd'
Copier après la connexion

好吧,欺骗大家感情了。在实际情况中可能会遇到如下调用闭包的函数,这样也是行的通的。

sayHello2('Bob')(); // calls the returned function without assignment
Copier après la connexion

Angular js 在$compile方法中使用上面的技术,可以将当前引用域传入到闭包中

$compile(template)(scope);
Copier après la connexion

意味着我们能够猜出他们的代码(简化)应该如下:

var $compile = function (template) {
// some magic stuff here// scope is out of scope, though...
return function (scope) {// access to `template` and `scope` to do magic with too};
};
Copier après la connexion

闭包并不一定需要返回函数。单纯在中间词汇域量的范围外简单访问变量就创造了一个闭包。

7、作用域和this关键字
根据函数被触发的方式不一样,每个作用域可以绑定一个不同的this值。我们经常使用this,但是我们并不是都了解其具体指代什么。 this默认是执行最外层的全局对象,windows对象。我们能够很容易的列举出不同触发函数绑定this的值也不同:

var myFunction = function () {
console.log(this); // this = global, [object Window]};
myFunction();
var myObject = {};
myObject.myMethod = function () {
console.log(this); // this = Object { myObject }};
var nav = document.querySelector('.nav'); // <nav class="nav">
var toggleNav = function () {
console.log(this); // this = <nav> element};
nav.addEventListener('click', toggleNav, false);
Copier après la connexion

在处理this值的时候,也会遇到问题。下面的例子中,即使在相同的函数内部,作用域和this值也会不同。

var nav = document.querySelector('.nav');
 // <nav class="nav">
var toggleNav = function () {
console.log(this); // <nav> element
setTimeout(function () {
console.log(this); // [object Window]}, 1000);
};
nav.addEventListener('click', toggleNav, false);
Copier après la connexion

发生了什么?我们创建了一个新的作用域且没有在event handler中触发,所以其得到预期的windows对象。如果想this值不受新创建的作用域的影响,我们能够采取一些做法。以前可能也你也见过,我们使用that创建一个对this的缓存引用并词汇绑定:

var nav = document.querySelector('.nav'); // <nav class="nav">
var toggleNav = function () {
var that = this;
console.log(that); // <nav> element
setTimeout(function () {
console.log(that); // <nav> element}, 1000);
};
nav.addEventListener('click', toggleNav, false);
Copier après la connexion

这是使用this的一个小技巧,能够解决新创建的作用域问题。

8、使用.call(), .apply() 和.bind()改变作用域
有时候,需要根据实际的需求来变化代码的作用域。一个简单的例子,如在循环中如何改变作用域:

var links = document.querySelectorAll('nav li');
for (var i = 0; i < links.length; i++) {
console.log(this); // [object Window]}
Copier après la connexion

这里的this并没有指向我们的元素,因为我们没有触发或者改变作用域。我们来看看如何改变作用域(看起来我们是改变作用域,其实我们是改变调用函数执行的上下文)。

9、.call() and .apply()
.call()和.apply()方法非常友好,其允许给一个函数传作用域来绑定正确的this值。对上面的例子我们通过如下改变,可以使this为当前数组里的每个元素。

var links = document.querySelectorAll('nav li');
for (var i = 0; i < links.length; i++) {
(function () {
console.log(this);
}).call(links[i]);}
Copier après la connexion

能够看到刚将数组循环的当前元素通过links[i]传递进去,这改变了函数的作用域,因此this的值变为当前循环的元素。这个时候,如果需要我们可以使用this。我们既可以使用.call()又可以使用.apply()来改变域。但是这两者使用还是有区别的,其中.call(scope, arg1, arg2, arg3)输入单个参数,而.apply(scope, [arg1, arg2])输入数组作为参数。

非常重要,需要注意的事情是.call() or .apply()实际已经已经取代了如下调用函数的方式调用了函数。

myFunction(); // invoke myFunction
可以使用.call()来链式调用:

myFunction.call(scope); // invoke myFunction using .call()
10、.bind()
和上面不一样的是,.bind()并不触发函数,它仅仅是在函数触发前绑定值。非常遗憾的是其只在 ECMASCript 5中才引入。我们都知道,不能像下面一样传递参数给函数引用:

// works
nav.addEventListener('click', toggleNav, false);
// will invoke the function immediately
nav.addEventListener('click', toggleNav(arg1, arg2), false);
Copier après la connexion

通过在内部创建一个新的函数,我们能够修复这个问题(译注:函数被立即执行):

nav.addEventListener('click', function () {
toggleNav(arg1, arg2);}, false);
Copier après la connexion

但是这样的话,我们再次创建了一个没用的函数,如果这是在循环中绑定事件监听,会影响代码性能。这个时候.bind()就派上用场了,在不需要调用的时候就可以传递参数。

nav.addEventListener('click', toggleNav.bind(scope, arg1, arg2), false);
Copier après la connexion

函数并没被触发,scope可以被改变,且参数在等着传递。

11、私有和公开作用域
在许多的编程语言中,存在public和private的作用域,但是在javascript中并不存在。但是在JavaScript中通过闭包来模拟public和private的作用域。

使用JavaScript的设计模式,如Module模式为例。一个创建private的简单方式将函数内嵌到另一个函数中。如我们上面掌握的,函数决定scope,通过scope排除全局的scope:

(function () {// private scope inside here})();
Copier après la connexion

然后在我们的应用中添加一些函数:

(function () {
var myFunction = function () 
{// do some stuff here};
})();
Copier après la connexion

这时当我们调用函数的时候,会超出范围。

(function () {var myFunction = function () {
// do some stuff here};
})();
myFunction(); // Uncaught ReferenceError: myFunction is not defined
Copier après la connexion

成功的创建了一个私有作用域。那么怎么让函公有呢?有一个非常好的模式(模块模式)允许通过私有和公共作用域以及一个object对象来正确的设定函数作用域。暂且将全局命名空间称为Module,里面包含了所有与模块相关的代码:

// define module
var Module = (function () {
return {myMethod: function () {
console.log('myMethod has been called.');}};
})();
// call module + methods
Module.myMethod();
Copier après la connexion

这儿的return 语句返回了公共的方法,只有通过命名空间才能够被访问到。这就意味着,我们使用Module 作为我们的命名空间,其能够包含我们需要的所有方法。我们可以根据实际的需求来扩展我们的模块。

// define module
var Module = (function () {
return {myMethod: function () {},
someOtherMethod: function () {}};})();
// call module + methods
Module.myMethod();
Module.someOtherMethod();
Copier après la connexion

那私有方法怎么办呢?许多的开发者采取错误的方式,其将所有的函数都至于全局作用域中,这导致了对全局命名空间污染。 通过函数我们能避免在全局域中编写代码,通过API调用,保证可以全局获取。下面的示例中,通过创建不返回函数的形式创建私有域。

var Module = (function () {
var privateMethod = function () {};
return {
publicMethod: function () {}};})();
Copier après la connexion

这就意味着publicMethod 能够被调用,而privateMethod 由于私有作用域不能被调用。这些私有作用域函数类似于: helpers, addClass, removeClass, Ajax/XHR calls, Arrays, Objects等。

下面是一个有趣事,相同作用域中的对象只能访问相同的作用域,即使有函数被返回之后。这就意味我们的public方法能够访问我们的private方法,这些私有方法依然可以起作用,但是不能够在全局左右域中访问。

var Module = (function () {
var privateMethod = function () {};
return {publicMethod: function () {
// has access to `privateMethod`, we can call it:
// privateMethod();}};})();
Copier après la connexion

这提供了非常强大交互性和安全性机制。Javascript 的一个非常重要的部分是安全性,这也是为什么我们不能将所有的函数放在全局变量中,这样做易于被攻击。这里有个通过public和private返回Object对象的例子:

var Module = (function () {
var myModule = {};
var privateMethod = function () {};
myModule.publicMethod = function () {};
myModule.anotherPublicMethod = function () {};
return myModule; // returns the Object with public methods})();
// usage
Module.publicMethod();
Copier après la connexion

通常私有方法的命名开头使用下划线,从视觉上将其与公有方法区别开。

var Module = (function () {
var _privateMethod = function () {};
var publicMethod = function () {};})();
Copier après la connexion

当返回匿名对象的时候,通过简单的函数引用赋值,Module可以按照对象的方式来用。

var Module = (function () 
{var _privateMethod = function () {};
var publicMethod = function () {};
return {
publicMethod: publicMethod,anotherPublicMethod: anotherPublicMethod}
})();
Copier après la connexion

以上就是关于JavaScript作用域的全部内容,希望对大家的学习有所帮助。

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!