JavaScript Learning record day6-function variable scope, deconstruction assignment and method

Source: Internet
Author: User
Tags function definition variable scope

JavaScript Learning record day6-function variable scope, deconstruction assignment and method

@ (learning) [JavaScript]

[TOC]

1. Scope

In JavaScript, variables that are var declared are actually scoped.

If a variable is declared inside the function body, the variable is scoped to the entire function body, and the variable is not referenced outside the body of the function:

‘use strict‘;function foo() {    var x = 1;    x = x + 1;}x = x + 2;   // ReferenceError: x is not defined 无法在函数体外引用变量x

If two different functions declare the same variable, the variable works only in the body of the function. In other words, the same name variables inside different functions are independent from each other:

function foo() {    var x = 1;    x = x + 1;    console.log(x);}function bar() {    var x = ‘A‘;    x = x + ‘B‘;    console.log(x);}foo();  // 2bar();  // AB

Because JavaScript functions can be nested, an intrinsic function can access variables defined by an external function, which in turn does not:

function foo() {    var x = 1;    function bar() {        var y = x + 1;  // bar可以访问foo的变量x    }    var z = y + 1;  // ReferenceError: y is not defined, foo不可以访问bar的变量y}foo();

What if the names of the variables for the intrinsic and external functions are identical? To test:

‘user strict‘;function foo() {    var x = 1;    function bar() {        var x = ‘A‘;        console.log(‘x in bar() = ‘ + x);    }    console.log(‘x in foo() = ‘ + x);    bar();}foo();// 结果:// x in foo() = 1// x in bar() = A

This means that JavaScript functions look for variables when they are found, starting with their own function definitions, and from "inside" to "outside". If an intrinsic function defines a variable with the same name as an external function, the variable of the intrinsic function will "mask" the variable of the outer function.

2. Variable Promotion

JavaScript's function definition has a feature that scans the entire function body's statement and "promotes" all declared variables to the top of the function:

‘use strict‘;function foo() {    var x = ‘Hello, ‘ + y;    console.log(x);    var y = ‘Bob‘;}foo();

Although it is a strict pattern, the statement var x = ‘Hello, ‘ + y; does not error because the variable is y declared later. But console.log shown Hello, undefined , y the value of the description variable is undefined . This is precisely because the JavaScript engine automatically promotes the declaration of the variable y, but does not raise y the value of the variable.

For the above foo() functions, the JavaScript engine sees the code equivalent to:

function foo() {    var y; // 提升变量y的申明,此时y为undefined    var x = ‘Hello, ‘ + y;    console.log(x);    y = ‘Bob‘;}

Because of this bizarre "feature" of JavaScript, when we define variables inside a function, strictly abide by the "first declaration of all variables within a function" rule. The most common practice is to declare all the variables used inside the function with a var:

function foo() {    var        x = 1, // x初始化为1        y = x + 1, // y初始化为2        z, i; // z和i为undefined    // 其他语句:    for (i=0; i<100; i++) {        ...    }}
3. Global scope

Variables that are not defined within any function have global scope. In fact, JavaScript has a global object by default window , and the global scope variable is actually bound to window one of the properties:

‘use strict‘;var course = ‘Learn JavaScript‘;console.log(course); // ‘Learn JavaScript‘console.log(window.course); // ‘Learn JavaScript‘

Therefore, direct access to global variables course and access window.course is exactly the same.

Because a function definition has two ways, a function defined as a variable is var foo = function () {} actually a global variable, so the definition of the top-level function is also treated as a global variable and bound to the window object:

‘use strict‘;function foo() {    alert(‘foo‘);}foo(); // 直接调用foo()window.foo(); // 通过window.foo()调用

The alert () function that we call each time is actually a variable of window:

‘use strict‘;window.alert(‘调用window.alert()‘);// 把alert保存到另一个变量:var old_alert = window.alert;// 给alert赋一个新函数:window.alert = function () {}alert(‘无法用alert()显示了!‘);// 恢复alert:window.alert = old_alert;alert(‘又可以用alert()了!‘);

This means that JavaScript actually has only one global scope. Any variable (the function is also considered a variable), if not found in the current function scope, will continue to look up, and finally, if not found in the global scope, the ReferenceError error is reported.

4. Name space

Global variables are bound to window , and different JavaScript files that use the same global variables, or the top-level functions that define the same name, can cause naming conflicts and are difficult to find.

One way to reduce the conflict is to bind all of your variables and functions to a global variable. For example:

// 唯一的全局变量MYAPP:var MYAPP = {};// 其他变量:MYAPP.name = ‘myapp‘;MYAPP.version = 1.0;// 其他函数:MYAPP.foo = function () {    return ‘foo‘;};

Putting all your own code into a unique namespace MYAPP can greatly reduce the likelihood of global variable collisions.

Many well-known JavaScript libraries are doing this: Jquery,yui,underscore and so on.

5. Local scope

Since the variable scope of JavaScript is actually inside the function, we for cannot define variables with local scope in the block of statements such as loops:

‘use strict‘;function foo() {    for (var i=0; i<100; i++) {        //    }    i += 100; // 仍然可以引用变量i}

To solve the block-level scope, ES6 introduces a new keyword let , which let can be used instead var to declare a block-scoped variable:

‘use strict‘;function foo() {    var sum = 0;    for (let i=0; i<100; i++) {        sum += i;    }    // SyntaxError:    i += 1;}
6. Constants

Because var and let declaration is a variable, if you want to declare a constant, before ES6 is not possible, we usually use all uppercase variables to represent "This is a constant, do not modify its value":

var PI = 3.14;

The ES6 Standard introduces new keywords const to define constants, const and let both have block-level scopes:

‘use strict‘;const PI = 3.14;PI = 3; // 某些浏览器不报错,但是无效果!PI; // 3.14
7. Deconstruction Assignment

Starting with ES6, JavaScript introduces an understanding of construct assignments, which can be assigned to a set of variables at the same time.

What is a deconstructed assignment? Let's take a look at the traditional way of assigning the elements of an array to several variables individually:

var array = [‘hello‘, ‘JavaScript‘, ‘ES6‘];var x = array[0];var y = array[1];var z = array[2];

Now, in ES6, you can assign values directly to multiple variables using a deconstructed assignment:

‘use strict‘;// 如果浏览器支持解构赋值就不会报错:var [x, y, z] = [‘hello‘, ‘JavaScript‘, ‘ES6‘];// x, y, z分别被赋值为数组对应元素:console.log(‘x = ‘ + x + ‘, y = ‘ + y + ‘, z = ‘ + z);

<font color= "Red" > note </font>:
When an array element is deconstructed and assigned, multiple variables are [...] enclosed.

If the array itself is nested, you can also use the following form to deconstruct the assignment, noting that the nesting level and position should be consistent:

let [x, [y, z]] = [‘hello‘, [‘JavaScript‘, ‘ES6‘]];x; // ‘hello‘y; // ‘JavaScript‘z; // ‘ES6‘

Deconstruction assignments can also omit certain elements:

let [, , z] = [‘hello‘, ‘JavaScript‘, ‘ES6‘]; // 忽略前两个元素,只对z赋值第三个元素z; // ‘ES6‘

If you need to remove several properties from an object, you can also use a deconstructed assignment to quickly get the specified properties of an object:

‘use strict‘;var person = {    name: ‘小明‘,    age: 20,    gender: ‘male‘,    passport: ‘G-12345678‘,    school: ‘No.4 middle school‘};var {name, age, passport} = person;// name, age, passport分别被赋值为对应属性:console.log(‘name = ‘ + name + ‘, age = ‘ + age + ‘, passport = ‘ + passport);

Sometimes, if a variable has been declared and assigned again, the correct wording will also report a syntax error:

// 声明变量:var x, y;// 解构赋值:{x, y} = { name: ‘小明‘, x: 100, y: 200};// 语法错误: Uncaught SyntaxError: Unexpected token =

This is because the JavaScript engine treats the statement beginning with {as a block, so = is no longer legal. The workaround is to enclose them in parentheses:

({x, y} = { name: ‘小明‘, x: 100, y: 200});

Deconstruct assignment Usage Scenarios

Deconstructed assignments can greatly simplify code in many cases. For example, to exchange two variables x and y values, you can write this and no longer need temporary variables:

var x=1, y=2;[x, y] = [y, x]

Quick access to the current page's domain name and path:

var {hostname:domain, pathname:path} = location;

If a function receives an object as a parameter, you can use deconstruction to bind the object's properties directly to the variable. For example, the following function can quickly create an Date object:

function buildDate({year, month, day, hour=0, minute=0, second=0}) {    return new Date(year + ‘-‘ + month + ‘-‘ + day + ‘ ‘ + hour + ‘:‘ + minute + ‘:‘ + second);}

Its convenience is that the incoming object only needs year , month and day these three properties:

buildDate({ year: 2017, month: 1, day: 1 });// 2017-12-31T16:00:00.000Z (UTC)

You can also pass in hour , minute and second properties:

buildDate({ year: 2018, month: 1, day: 1, hour: 20, minute: 15 });// 2018-01-01T12:15:00.000Z (UTC)

Using a deconstructed assignment can reduce the amount of code, but it needs to work correctly in a modern browser that supports the ES6 deconstruction assignment feature. Currently, browsers that support deconstruction assignments include Chrome,firefox,edge, and so on.

8. Methods

A method that binds a function in an object called this object.

In JavaScript, the definition of an object is this:

var xiaoming = {    name: ‘小明‘,    birth: 1990};

However, if we xiaoming bind a function, we can do more things. For example, write a age() method that returns xiaoming the age:

var xiaoming = {    name: ‘小明‘,    birth: 1990,    age: function () {        var y = new Date().getFullYear();        return y - this.birth;    }};console.log(xiaoming.age); // [Function: age]console.log(xiaoming.age()); // 当前调用是28,下一年调用就变成29了

A function that is bound to an object is called a method, and it is no different from a normal function, but it uses a this keyword internally.

Inside a method this is a special variable that always points to the current object, which is xiaoming the variable. So, this.birth you can get xiaoming the birth properties.

Let's take it apart and write:

function getAge() {    var y = new Date().getFullYear();    return y - this.birth;}var xiaoming = {    name: ‘小明‘,    birth: 1990,    age: getAge};console.log(xiaoming.age()); // 25, 正常结果console.log(getAge()); // NaN

If the function inside JavaScript is called this , then this who does this point to?

The answer is, the situation is set!

If called as a method of an object, for example xiaoming.age() , this function's this point points to the object being called, that xiaoming is, this is in line with our expectations.

If a function is called individually, for example, getAge() at this point, the function points to the this global object, that is window .

If this is the case:

var fn = xiaoming.age; // 先拿到xiaoming的age函数fn(); // NaN

It's not going to work! To ensure that this the point is correct, it must be obj.xxx() called in the form!

Since this is a huge design error, it's not that simple to correct. The ECMA decides to strict make the function point in the pattern this undefined so that, in the strict mode, you get an error:

‘use strict‘;var xiaoming = {    name: ‘小明‘,    birth: 1990,    age: function () {        var y = new Date().getFullYear();        return y - this.birth;    }};var fn = xiaoming.age;fn(); // TypeError: Cannot read property ‘birth‘ of undefined

The decision was to expose the error in time and did not solve this the correct position to point to.

There are times when you refactor a method like refactoring:

‘use strict‘;var xiaoming = {    name: ‘小明‘,    birth: 1990,    age: function () {        function getAgeFromBirth() {            var y = new Date().getFullYear();            return y - this.birth;        }        return getAgeFromBirth();    }};xiaoming.age(); // TypeError: Cannot read property ‘birth‘ of undefined

The result is another error! The reason is that the this pointer is age pointing only inside the function of the method, the xiaoming function defined inside the function, and this pointing again undefined ! (in non- strict modal, it points back to the global object window !) )

There is no way to fix it, we use a that variable first to capture this :

‘use strict‘;var xiaoming = {    name: ‘小明‘,    birth: 1990,    age: function () {        var that = this; // 在方法内部一开始就捕获this        function getAgeFromBirth() {            var y = new Date().getFullYear();            return y - that.birth; // 用that而不是this        }        return getAgeFromBirth();    }};console.log(xiaoming.age()); // 28

var that = this;, you can safely define other functions within the method, rather than piling all the statements into a single method.

9. apply

Although in a separate function call, depending on whether it is a strict pattern, this point to undefined or window , however, we can still control this the point!

To specify this which object the function points to, you can use the method of the function itself, apply which receives two arguments, the first parameter is the variable that needs to be bound, and the this second argument is the Array argument that represents the function itself.

applyCall with repair getAge() :

function getAge() {    var y = new Date().getFullYear();    return y - this.birth;}var xiaoming = {    name: ‘小明‘,    birth: 1990,    age: getAge};console.log(xiaoming.age()); // 28console.log(getAge.apply(xiaoming, [])); // 28, this指向xiaoming, 参数为空

Another way to do apply() This is call() that the only difference is:

apply()Package the parameters into a Array re-incoming;

call()The parameters are passed in order.

such as calls Math.max(3, 5, 4) , respectively, apply() and call() implemented as follows:

console.log(Math.max.apply(null, [3, 5, 4])); // 5console.log(Math.max.call(null, 3, 5, 4)); // 5

For normal function calls, we usually this bind to null .

10. Decorative Device

Using apply() , we can also dynamically change the behavior of the function.

All JavaScript objects are dynamic, even if built-in functions, we can also re-point to the new function.

Now suppose we want to count how many times the code has been called parseInt() , to find out all the calls, and then add them manually count += 1 , but that's silly. The best solution is to replace the default with our own function parseInt() :

‘use strict‘;var count = 0;var oldParseInt = parseInt; // 保存原函数function parseInt() {}window.parseInt = function () {    count += 1;    return oldParseInt.apply(null, arguments); // 调用原函数};// 测试:parseInt(‘10‘);parseInt(‘20‘);parseInt(‘30‘);console.log(‘count = ‘ + count); // 3

Learning Reference Tutorial: http://www.liaoxuefeng.com

JavaScript Learning record day6-function variable scope, deconstruction assignment and method

Related Article

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.