2

JavaScript 函数、作用域和继承

 2 years ago
source link: https://blogread.cn/it/article/7289?f=hot1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JavaScript 函数、作用域和继承

浏览:1850次  出处信息

关于函数、作用域和继承,可以写的非常多。不过和 JavaScript 类型浅解 一样,是写给初学者看的,我们着重从简单的来。当然,即使用「简单」来描述,这也是 JavaScript 中最不容易懂的点之一。

如你所见,function fn(){},像这个声明式函数。这几乎是函数最常见的内容。一个函数可以有多种形式。虽然初学看起来有点乱,不过我想说的是,你总会知道如何用的,现在知道就可以了。它们除了在 hositing (后面说)表现会有一些不同,其他的都差不多,后面你总会知道如何合理地使用他们的。

1. 声明/匿名函数

当声明函数没有 name 的时候,便成了匿名函数(name)。作为 first-class object,像其他对象一样,它也可以作为值传递给一个变量,作为函数的参数。

function [name]([arg1] [, arg2] [..., argN]) {
     statements
}

在 ES6 中,我们可能会使用像 Coffee Script 中类似的语法:

([arg1] [, arg2]) => {
   statements
}

2. Function 构造函数

new Function([arg1] [, arg2] [..., argN], statement);

上面的代码是表现形式,是壳。而理解函数的重点,还在于里面。这个里面指的是用 {} 包起来的一切,有个花括号乱起来的地方 —— 函数体。以上面的形式为例,在函数体内我们可以直接使用的有:

  • 参数:可以像变量(argN)一样在函数体中使用

  • 函数名:可以用函数名或者 arguments.callee 调用自身

  • 上下文:每一个函数体,都是一个作用域,this 代表当前作用域上下文

  • 参数对象:arguments 对象按顺序存储着函数变量

让我们直观一点,用一个代码片断来描述上面的内容:

function sofish(gender, age) {
  console.log(gender, age); // male, 28
  console.log(sofish === arguments.callee) // true
  console.log(this); // window
  console.log(arguments); // ['male', 28]
}

二、作用域

作用域可以这样理解 —— 封闭的,不影响外部的空间。不过在 JavaScript 函数可以嵌套,也就是说作用域是可以嵌套的。而为了更好也把使用域联系起来,它使用了一个在多数现代码语言中都实行的理念 —— 闭包。如果用一句话来解析闭包 —— 外部不能访问内部变量,内部可以外部变量。考虑一下如下代码:

(function sofish(){

  var nickname = ‘小鱼’;
  
  (function ciaocc() {
    var age = 24;
    
    // 内部函数可以访问外部 sofish() 创建作用域的变量
    console.log(nickname); // '小鱼'
  })();
  
  // 但是外部函数,不能访问内嵌函数 ciaocc() 创建使用域的变量
  console.log(age); // ReferenceError: age is not defined
})()

从作用域的类型看来,总的来说,在 JavaScript 有两种形式的使用域 —— 全局/函数级使用域。换句话说,除了全局作用域,只有函数级作用域,像 if 、while 、for...in 等代码块是不会创建作用域的。你可以试试运行这段代码:

var sofish = '小鱼'; 

while(sofish === '小鱼') {
  var cc = 'ciaocc';
  sofish = 'whatever';
}

console.log(cc); // 'ciaocc'

说到这里,来理解个非常重要的知识点 —— hoisting。像上面的代码都是按顺序执行的,在同一个作用域,这是比较容易理解的。不过,考虑一下下面这段代码的执行结果:

console.log(typeof sofish); // 1___
console.log(momo); // 2___
function sofish() {
  var ciaocc = 'a beauty';
};
sofish();
var momo;
console.log(ciaocc); // 3___

第 1 个输出的是 'function',第 2 个输出的是 undefined,但为什么最后一个是 Error —— ReferenceError: ciaocc is not defined。结果似乎有点出乎意料。这就是变量、函数 hoisting 导致的结果,我们可以这样理解 —— 函数和变量在代码解析的时候,会提到作用域的最顶端,如上面的代码,执行的时候机器看到的代码是其实是这样的:

// 函数和变量被提到最顶端
function sofish() {
  var ciaocc = 'a beauty';
};
var momo;

console.log(typeof sofish); // 1___
console.log(momo); // 2___

sofish();
console.log(ciaocc); // 3___

不过,函数和变量只会被提到 所在作用域 的最顶端,因此上面的函数 sofish() 中的变量 ciaocc 只存在所在作用域顶端,不会提到外部的顶端。聪明你的可能会突然想起,函数和变量都会进行 hoisting,那么哪个在最顶部呢?考虑一下下面的代码:

console.log(sofish);

function sofish() {};
var sofish = 'ciao cc';

三、继承与构造函数

这里推荐几篇让人更容易理解的文章,深入浅出,我觉得写的非常好:

Javascript继承机制的设计思想

Javascript 面向对象编程:封装构造函数继承非构造函数继承

全篇讲的比较散,有问题的话,可以自己看看下面推荐的一些文章,或者留言之类,虽然基本上是写给某位看的,不过既然分享出来,我也会抽空回答一些问题。

Function and Function Scope

ECMAScript 5 Strict Mode, JSON, and More

Named function expressions demystified

建议继续学习:

QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK