js函数

函数:

函数的三种声明方式:

(1)function 命令

function print(s) {
  console.log(s);
}  //  直接生命  直接使用print( 参数) 调用

(2)函数表达式

除了用function命令声明函数,还可以采用变量赋值的写法。

var print = function(s) {
  console.log(s);
};  //  相当于将函数赋值给某个变量

注意点:

采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

例如:

var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined

print()
// function

上面代码在函数表达式中,加入了函数名x。这个x只在函数体内部可用,指代函数表达式本身,其他地方都不可用。这种写法的用处有两个,一是可以在函数体内部调用自身,二是方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)。因此,下面的形式声明函数也非常常见。

var f = function f() {};

(3)Function 构造函数

var add = new Function(
  'x',
  'y',
  'return x + y'
);

// 等同于
function add(x, y) {
  return x + y;
}

上面代码中,Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数。

你可以传递任意数量的参数给Function构造函数,只有最后一个参数会被当做函数体,如果只有一个参数,该参数就是函数体。

var foo = new Function(
  'return "hello world";'
);

// 等同于
function foo() {
  return 'hello world';

注意:函数重复声明之后,后面的生命会覆盖前面的声明

调用函数时直接使用圆括号运算符,return语句返回和递归。

调用函数时,需要使用圆括号,括号之中可以添加函数需要的参数。

代码中的return语句,表示返回,JavaScript引擎遇到return语句,就直接返回return后面的表达式的值。没有return的话就返回undefined。

函数在JavaScript中被成为第一等公民。

函数名提示:有个疑问?

var 的变量名和 一个函数名 ,他们两个谁提升的优先级高?如果是相同的名字,他们两个谁会覆盖谁?

下图这个不是很明白。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ESJiX2x5-1610200578102)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210105194033765.png)]

匿名函数:直接使用函数赋值给一个声明的变量。具名函数:含有函数名的函数。

函数的名字name,函数长度length,函数的输出打印toString()等属性等。

函数的作用域:

作用域是指的是变量存在的范围,在基础中,变量只有两个作用域,一个是全局作用域,变量在整个程序中一直存在,所有地方都可以读取,一个是局部作用域,变量只存在函数内部读取,

函数内部的变量提升,与全局作用域一样,函数作用域内部也会产生‘’变量提升‘’现象,var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

function foo(x) {
  if (x > 100) {
    var tmp = x - 100;
  }
}

// 等同于
function foo(x) {
  var tmp;
  if (x > 100) {
    tmp = x - 100;
  };
}

函数本身的作用域

函数本身也是一个值,也有自己的作用域,它的作用域与变量一样,就是其声明时所在的作用域,与其运行是所在的作用域无关

var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1
var x = function () {
  console.log(a);
};

function y(f) {
  var a = 2;
  f();
}

y(x)
// ReferenceError: a is not defined

函数的length属性与实际传入的参数个数无关,只反映函数预期传入的参数个数。

如果有同名的参数,则取最后出现的那个值。

上面代码中,函数f()有两个参数,且参数名都是a。取值的时候,以后面的a为准,即使后面的a没有值或被省略,也是以其为准。

function f(a, a) {
  console.log(a);
}

f(1) // undefined

调用函数f()的时候,没有提供第二个参数,a的取值就变成了undefined。这时,如果要获得第一个a的值,可以使用arguments对象。

function f(a, a) {
  console.log(arguments[0]);
}

f(1) // 1

arguments对象

定义:

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。

(2)与数组的关系

需要注意的是,虽然arguments很像数组,但它是一个对象。数组专有的方法(比如sliceforEach),不能在arguments对象上直接使用。

如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。下面是两种常用的转换方法:slice方法和逐一填入新数组。

var args = Array.prototype.slice.call(arguments);

// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
  args.push(arguments[i]);
}

闭包:

如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

function f1() {
  var n = 999;
  function f2() {
  console.log(n); // 999
  }
}

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是 JavaScript 语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999

闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7

立即调用的函数表达式

根据 JavaScript 的语法,圆括号()跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

为了避免解析的歧义,JavaScript 规定,如果function关键字出现在行首,一律解释成语句。因此,引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。

函数定义后立即调用的解决方法,就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。

(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。

推而广之,任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

甚至像下面这样写,也是可以的。

!function () { /* code */ }();
~function () { /* code */ }();
-function () { /* code */ }();
+function () { /* code */ }();

只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

// 写法一
var tmp = newData;
processData(tmp);
storeData(tmp);

// 写法二
(function () {
  var tmp = newData;
  processData(tmp);
  storeData(tmp);
}());

没有了解过的知识:

eval命令,基本用法:eval命令接受一个字符串作为参数,并将这个字符串当做语句来执行。

以及使用别名调用等。

你可能感兴趣的:(js,js,面向对象编程,javascript)