美文网首页
Web18.函数与作用域

Web18.函数与作用域

作者: FiredEarthMusic | 来源:发表于2017-11-09 21:54 被阅读4次

IIFE
http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife

知识点:
1.函数的声明方式
2.参数 返回值
3.声明前置
4.作用域
5.立即执行函数表达式
6.递归

函数

JavaScript函数指一个特定代码块,可能包含多条语句,
可以通过名字来供其他语句调用以执行函数包含的代码
语句。

function doSomething() {
    statment1;
    statment2;
    statment3;
}
doSomething();
//三种声明函数的方式

1.构造函数
首先函数也是对象的一种,我们可以通过其构造函数
使用new来创建一个函数对象

//不推荐使用
var sayHello = new Function("console.log('hello world');")


2.函数声明
//使用function关键字可以声明一个函数

//函数声明
function sayHello(){
    console.log('hello')
}
//函数调用
sayHello()
声明不必放在调用前面


3.函数表达式
var sayHello = function(){
    console.log('hello');
}
sayHello()
声明必须放到调用的前面
本质上是一个表达式
参数
function sayHello(name){
    console.log('hello' + name)
}

sayHello('fem')
sayHello('lem')
//声明的时候不执行
//调用的时候才执行


多个参数
function printInfo(name, age, sex){
    console.log(name);
    console.log(age);
    console.log(sex);
}
printInfo('fem', 25, 'man')
//只是按次序传递
arguments

//在函数内部,你可以使用arguments对象获取到
//该函数的所有传入参数
function printInfo(name, age, sex){
    console.log(name);
    console.log(age);
    console.log(sex);
    console.log(arguments);
    
    console.log(arguments[0])
}
printInfo(2, 'boy')
//arguments 类数组对象
重载
在JS中 没有重载! 同名函数会覆盖
但可以在函数体针对不同的参数调用执行相应的逻辑

function printPeopleInfo(name, age, sex){
    if(name){
        console.log(name);
    }
    if(age){
        console.log(age);
    }
    if(sex){
      console.log(sex);
    }
}
printPeopleInfo('Byron', 26);
printPeopleInfo('Byron', 26, 'male');
返回值
//有时候我们希望在函数执行后得到一个结果
//供别人使用,可以通过return实现

function sum(a, b){
    a++;
    b++;
    return a + b;
}
var result = fn(2, 3);
console.log(result);

//注意点!
//1.如果不写return语句,函数也会默认给我们返回undefined
//2.函数在执行过程中,只要遇到return就会立即结束退出函数体

function fn(a){
    if(a < 0){
        return;
    }
   //下面没用else,但效果一样
   a++;
   return a + a;
 }

 //3.函数的返回值和console.log() 是两个不同的东西
 千万不要
  function getAge(age){
     return console.log(age);
  }
 应该
   function getAge(age){
     console.log(age);
      return age;
  }
声明前置

var和function的声明前置
在一个作用域下  var声明的变量和function声明的函数会前置

console.log(a);  //undefined
var a = 3;
console.log(a);  //3

sayHello();

function sayHello(){
    console.log('hello')
}

实际上是
var a
function sayHello(){

}
a = 3
sayHello()
console.log(fn);  //undefined
fn();  //报错

var fn = function() { }
函数表达式和var一个变量没有区别
先有 再用
函数内部的声明前置
function fn(){
    console.log(a)  //undefined
    var a = 3
    console.log(a)
}
当命名冲突时
先前置 再覆盖

var fn = 3;
function fn() {}

console.log(fn);  //3


function fn(){}
var fn = 3;

console.log(fn);  //3
参数重名
function fn(fn){
    console.log(fn)

    var fn = 3;
    console.log(fn);
}

//相当于
//function fn(10){
//    var fn = 10
//    var fn
//    console.log(fn)
//    fn = 3
//    console.log(fn)
//}

fn(10)   //10  3
ES5以前

作用域
在JS中只有函数作用域,没有块作用域
函数才有作用域
function fn(){
    var a = 1;
    
    if( a > 2){
        var b = 3;  //也会声明前置
                         // b = 3没有执行
    }
    console.log(b);
}
fn();    //undefined

console.log(a)  //"ReferenceError: a is not defined"
                        //全局作用域下没有a
声明一个已经存在的变量
function fn(){}
var fn
console.log(fn)

var a = 1
var a
var a
console.log(a)

var 重复声明一个已经存在的变量,原变量值不变
不加var作用
function fn(){
    a = 1;
}
fn();
console.log(a);    //1

可以看到不写var会声明一个全局的变量,
这是我们在编程中应该要避免的,即使真
的需要全局变量,也应该在最外层作用域使用var声明
递归
1.自己调用自己
2.设定终止条件
3.优点 算法简单
4.缺点 效率低


求n的阶乘n!
function factor(n){
    if( n === 1) {
        return 1
    }
    return n * factor(n-1)
}
factor(5)

求1+2+...+n的值
function sum(n){
    if( n === 1) {
        return 1
    }
    return n + sum( n - 1)
}
sum(10)
立即执行函数表达式

(function say(){
    var a = 1;
})()

(function(){
    var a = 1;
})()

(function(){
    var a = 1;
})()
console.log(a);  //undefined
//作用: 隔离作用域

其他写法
(function fn1() {});

//在数组初始化器内只能是表达式
[function fn2() {}];

//逗号也能操作表达式
1, function fn3() {};

!function(){
    var a = 1;
}
只有函数才有作用域
console.log(j);  //undefined
console.log(i);  //undefined

//var i 也是全局变量
for(var i = 0;  i<10; i++){
    var j = 100;
}
console.log(i);  //10
console.log(j);  //100
fn();

    var i = 10;
    var fn = 20;
    console.log(i);
    function fn(){
        console.log(i);
        var i = 99;
        fn2();
        console.log(i);
        function fn2() {
           i = 100;
        }
    }

作用域链

1.执行上下文 executionContext
2.活动对象
3.Scope属性

var x = 10
bar()
function foo() {
    console.log(x)
}
function bar() {
    var x = 30
    foo()  //输出什么
}

globalContext = {
    AO: {
        x: 10
        foo: function
        bar: function
    },
    Scope: null 
}
//声明 foo时 得到下面
foo.[[scope]] = globalContext.AO
//声明bar时 得到下面
bar.[[scope]] = globalContext.AO

//注意 在当前的执行上下文内声明的函数,这个函数的[[scope]]就执行当前执行上下文的AO

当调用bar() 时 进入bar的执行上下文
barContext = {
    AO: {
        x: 30
    },
    Scope: bar.[[scope]]  //globalContext.AO
}


当调用foo() 时,先从bar执行上下文中的AO里面找,
找不到再从bar的[[scope]]里面找 找到后即调用


当调用foo() 时,进入foo的执行上下文
fooContext = {
    AO: {},
    Scope: foo.[[scope]]  //globalContext.AO
}
所以console.log(x)是10
var x = 10;
bar()    //输出什么
function bar(){
    var x = 30;
    function foo() {
        console.log(x)
    }
    foo();
}

1.
globalContext = {
    AO: {
        x: 10
        bar: function(){}
    }
}
bar.[[scope]] = globalContext.AO

barContext = {
    AO:{
        x: 30
        foo: function(){}
    },
    Scope: bar.[[scope]] = globalContext.AO
}
foo.[[scope]] = barContext.AO


fooContext = {
    AO: {},
    Scope: foo.[[scope]] = barContext.AO
}
//30
var a = 1;

function fn(){
    console.log(a);
    var a = 5;
    console.log(a);
    a++;
    var a;
    fn3();
    fn2();
    console.log(a);

    function fn2(){
        console.log(a);
        a = 20;
    }
}

function fn3(){
    console.log(a)
    a = 200;
}

fn();
console.log(a);

globalContext = {
    AO: {
        a: 200,
        fn: function
        fn3: function
    }
}

fn.[[scope]] = globalContext.AO
fn3.[[scope]] = globalContext.AO

fnContext = {
    AO: {
        a: 6
        fn2: function(){}
    },
    Scope: globalContext.AO
}

fn2.[[scope]] = fnContext.AO

fn3Context = {
    AO: {},
    Scope: globalContext.AO
}
fn2Context = {
    AO: {},
    Scope: globalContext.AO
}

//undefined 5 1 6 20 200

题目1:函数声明和函数表达式有什么区别

函数声明:
function sayHello(){
    console.log('hello')
}
sayHello()
//声明不必放在调用的前面,但要在同一个作用域下

函数表达式:
var sayHello = function(){
    console.log('hello');
}
sayHello()
//声明必须放在调用的前面

题目2:什么是变量的声明前置?什么是函数的声明前置

变量声明前置:
js引擎工作方式是,先解析代码,获取所有被声明的变量,然后
再逐行运行,造成的结果是,所有的变量的声明语句,都会被提
升到代码的头部,这就叫做变量提升

函数声明前置:
和变量声明前置类似,如果我们使用函数声明的方式,那么即使函数
写在最后也可以在前面语句调用,前提是函数声明部分已经被下载到
本地

fn()
function fn(){
    console.log('1');
}

题目3:arguments 是什么

//在函数内部,可以使用arguments对象获取到该函数的所有传入参数
//arguments是一个类数组对象

function printPersonInfo(name,age,sex){
    console.log(name);
    console.log(age);
    console.log(sex);
    console.log(arguments);
    console.log(arguments[0]);
}

function printPersonInfo(){
    console.log(arguments[0])
}
printPersonInfo(2,'boy')


//arguments[0]  类数组对象可使用该方法
//arguments[1]

题目4:函数的"重载"怎样实现

//重载是函数具有相同的名字,但是由于传入的参数不同,执行不同操作。在js中没有类似其他语言的重载,因为同名函数会被覆盖。但是js可以通过在函数内部对传入参数进行判断来达到重载的目的
//js没有重载,同名函数会覆盖,
//但可以在函数体针对不同的参数调用执行响应的逻辑


function printPeopleInfo(name, age, sex){
    if(name){
        console.log(name);
    }
    if(age){
        console.log(age);
    }
    if(sex){
        console.log(sex);
    }
}
printPeopleInfo('Byron', 26);
printPeopleInfo('Byron', 26, 'male');

题目5:立即执行函数表达式是什么?有什么作用

(function(){
    var a = 1;
})()

//隔离作用域,避免变量污染全局

题目6:求n!,用递归来实现

function factor(n) {
    if( n === 1){
        return 1;
    }else{
        return n * factor(n-1);
    }
}

//不递归
function factor(n){
    var result = 1;
    for(var i=1; i<n+1; i++){
        result = i*result
    }
    return result;
}

题目7:以下代码输出什么?

7.png
结果:
getInfo('饥人谷', 2, '男');

//name: 饥人谷
//age: 2
//sex: 男
//arguments(3)['饥人谷', 2, '男']
//name: valley


getInfo('小谷', 3);

//name:小谷
//age: 3
//sex: undefined
//arguments(2)['小谷', 3]
//name: valley


getInfo('男');

//name: 男
//age: undefined
//sex: undefined
//arguments['男']
//name: valley

题目8:写一个函数,返回参数的平方和?

8.png
function sumOfSquares(){
    var h = 0;
    for(var i=0; i<arguments.length; i++){
        h = h + arguments[i] * arguments[i]
    }
    return h
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result)    //29
console.log(result2)  //10
//更严谨的写法
function sumOfSquares(){
    var res = 0;
    for (var i in arguments){
        if (typeof arguments[i] !== 'number') {
            return 'you entered a wrong number';
        }
    }
    for (var j in arguments){
        res += arguments[j]*arguments[j];
    }
    return res;
}

题目9:如下代码的输出?为什么

9.png
console.log(a)   //undefined
console.log(b)   //Reference error

题目10:如下代码的输出?为什么

10.png
输出: 
//hello world
//Uncaught TypeError: sayAge is not a function
//第一个是函数声明   调用可以放在任何地方
//第二个是函数表达式  调用必须放在声明之后

题目11. 如下代码输出什么? 写出作用域链查找过程伪代码

11.png
先声明前置

globalContext = {
    AO: {
        x: 10
        foo: function
        bar: function
    },
    Scope: null
}
//声明foo时  得到下面
foo.[[scope]] = globalContext.AO
//声明foo时  得到下面
bar.[[scope]] = globalContext.AO

//注意: 在当前的执行上下文内声明的函数,
//这个函数的[[scope]]就执行当前执行上下文的AO


barContext = {
    AO: {
        x: 30
    },
    Scope: bar.[[scope]]  
}
fooContext = {
    AO: null
   Scope: foo.[[scope]]
}

//结果为10

题目12. 如下代码输出什么? 写出作用域链查找过程伪代码

12.png
globalContext{
    AO: {
        x: 10
        bar: function
    },
    Scope: null
}
bar.[[Scope]] = globalContext.AO

barContext {
    AO: {
        x: 30
        foo: function
    },
    Scope: bar.[[scope]] = globalContext.AO
}

foo.[[scope]] = barContext.AO

fooContext = {
    AO: {},
    Scope: foo.[[scope]] = barContext.AO
}

题目13. 如下代码输出什么? 写出作用域链查找过程伪代码

13.png

题目14. 如下代码输出什么? 写出作用域链查找过程伪代码

333FFDFC-57AB-4CB1-8E62-34094E970014.png
globalContext{
    AO:{
        a: 1
        fn: function
        fn3: function
    }
}
fn.[[scope]] = globalContext.AO
fn3.[[scope]] = globalContext.AO
 
fnContext{
    AO:{
       
    }
}
fn3Context{
     AO:{

      }
}


相关文章

  • Web18.函数与作用域

    IIFEhttp://benalman.com/news/2010/11/immediately-invoked-...

  • 执行上下文、作用域链、this、闭包

    javascript 执行的是词法作用域,即静态作用域。与之相对的是动态作用域。 静态作用域:函数的作用域在函数定...

  • 作用域和作用域链

    作用域和作用域链 作用域 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在Java...

  • 作用域

    何为作用域 任何编程语言都有作用域的概念,简单来说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可...

  • JavaScript中的作用域和作用域链(一)

    作用域 1.作用域的概念: 变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。 2.全局作用域与...

  • JavaScript静态作用域解析

    静态作用域与动态作用域 因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。而与...

  • 词法作用域 - 2023-02-09

    静态作用域与动态作用域 因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。而与...

  • JS基础知识(2) -- 作用域和作用域链

    作用域 作用域就是变量与函数的可访问范围 全局作用域 函数作用域 ES6块级作用域 ES5没有块级作用域,ES6有...

  • 作用域链

    一、作用域 定义:作用域就是变量与函数的可访问范围。作用域控制着变量与函数的可见性和生命周期。 1、全局作用域(G...

  • JavaScript作用域学习笔记

    @(JS技巧)[JavaScript, 作用域] JavaScript作用域学习笔记 概念: 作用域就是变量与函数...

网友评论

      本文标题:Web18.函数与作用域

      本文链接:https://www.haomeiwen.com/subject/mrmyuxtx.html