JavaScript函数

概述:

使用关键字“function”定义的一段具有独立作用域,能被反复执行的语句块。

函数中可以封装一些功能(代码),在需要时可以执行功能(代码)。

函数的声明:

1.利用关键字function声明

基本格式为:

function fn(){

}

2.利用函数表达式进行赋值声明

基本格式为:

var fnc=function () {

}

这种方式必须先声明后调用,否则会报错。

3.利用构造函数Function声明

基本格式为:

var fnc=new Function("","")

注意:

  1. 这种写法是将参数列表和函数体放置在了一起同样作为了参数。
  2. 如果只有一个参数,那这个参数就是函数体。(就是花括号里面的代码)
  3. 构造函数内的参数无论有多少个,始终会将其最后一个参数作为函数体去执行
  4. 参数和函数体的语句要作为字符串去呈现

 如果函数声明重复了,则只会调用最后声明的函数

函数的返回值

每个函数都会指定返回一个值,如果你未设置返回值,则返回undefined。

返回值关键字return,return后的值将作为函数的执行结果返回。

注意:return只会返回一个值,如果想返回多个值就必须将多个值放到一个数组或者对象返回;return后的语句将不会被执行;

function fn(n){
      return {"n的平方":n**2,"n的立方":n**3}
}
function fn(n){
   return [n**2,n**3]
}

拓展:break可以退出当前的循环,continue用于跳过当前循环,return可以结束整个函数。

函数的参数

1.函数的参数分为形参和实参;

​
function fn(n){
   return [n**2,n**3]
}
fn(3)
这里的n就是形参,fn(3)里面的3就是实参
​

函数的参数是不需要专门声明的;

2.对位传参法

function fn(name,sex,age){
  return console.log("我是"+name+",性别:"+sex+",今年:"+age+"岁。")
}
fn("张三","男",20)

这里运行的结果是:
我是张三,性别:男,今年20岁。

对位传参法有一个弊端,假如我传入的参数没有按照顺序传入,则会出现下面的情况:

function fn(name,sex,age){
  return console.log("我是"+name+",性别:"+sex+",今年:"+age+"岁。")
}
fn("男","张三",20)

这里运行的结果就是:
我是男,性别:张三,今年20岁。

所以这里就需要用下面的方法传参

3.对象传参法

对象传参法传入的参数是一个对象,例如:

function fn(obj){
  return console.log("我是"+obj.name+",性别:"+obj.sex+",今年:"+obj.age+"岁。")
}
fn({sex:"男",name:"张三",age;20})

这里运行的结果是:
我是张三,性别:男,今年20岁。

注意:我们可以给形参一个默认值,当执行这个函数如果没有给实参时就会显示默认值,例如(这里以对象传参作为例子,这种给默认值的方法是ES6以后的版本才能执行的,还有给默认值的方法是用三元表达式或者或运算“||”):

function fn(obj={name:"李四",sex:"男",age:23}){
  return console.log("我是"+obj.name+",性别:"+obj.sex+",今年:"+obj.age+"岁。")
}
fn()

这里运行的结果是:
我是李四,性别:男,今年23岁。

Arguments

arguments是返回函数的参数,它返回的值是类似一个数组的集合。

将其转换为真数组有两种方式

①:Array.from(aeguments);②Array.portotype.slice.call(aeguments);

例如:

function fn(){
   arguments=Array.portotype.slice.call(arguments)
   console.log(arguments)
}
fn(1,2,3,4,5)

这里的运行结果是:
[1,2,3,4,5]
function fn(){
   arguments=Array.from(arguments)
   console.log(arguments)
}
fn(1,2,3,4,5)

这里的运行结果是:
[1,2,3,4,5]

函数的作用域

全局作用域 & 函数作用域 & 块级作用域

块级作用域:只在if判断、for循环等语句里面有效,需要使用let关键字声明

函数作用域:只在某个函数里面有效,不管是var或者let都可以

全局作用域:在当前文件中的所有函数,块中都有效,不管let还是

 

  • 访问优先级:里层能访问外层,外层不能访问里层
  1. 块级能访问局部,局部能访问全局,
  2. 全局不能访问局部,局部不能访问块级 
  • 访问变量时,先在当前作用域查找,然后在去上层作用域查找

关于声明提升

变量提升只是提升的变量名,而函数则是整体提升;

函数递归

定义:在执行函数的时候自己调用自己;达到条件后结束。

例如:

用递归函数打印出n到m的数字之和(n<m);

function sum(n,m){
  if(m>n){return  m+sum(n,m-1);}
  else   { return  m; }
}
sum(1,100);

这里显示的结果就是5050;

注意:递归函数必须有结束条件,不然就会跟死循环出现一样的效果; 

自执行匿名函数

不需要调用,自己执行,例如:

(function(n){
    return  n
})(2);

这里它会自己执行并返回结果为2;

 自执行匿名函数的可以防止变量名的冲突;

回调函数

作为参数的函数就被称为回调函数;

例如:

function fn(){
   return 30;
}
function num(a,b){
  if(a=="ok"){
     console.log(b());
  }
  else{ 
     console.log(b);
  }
}

num("ok",fn);

这里控制台就会打印出30这个结果。这时这里的函数fn()就被称作回调函数。

注意:回调函数是在另一个函数里面进行调用并执行的,所以实际参数的也是由另一个函数负责传递的,我们把回调函数作为参数传入是只给函数名即可,不应该给实际参数。 

 函数的闭包

在函数中,需要在外部读取函数里面定义的局部变量时,我们可以定义一个闭包实现。

闭包的定义:在一个函数A中定义另一个函数B,在函数B中控制定义在函数A的变量,在函数A中返回函数B,再将这个函数B返回给调用方,这就叫做闭包。

例如:

function fn(){
  var a=0;
   function fn1(){
     return a;
   }
  return fn1;
}
fn()();

这时将会返回a的值0;

另外可以给a赋值,比如:

function fn(){
 var a=0;
  function fn1(num){
   a= num==undefined? a:num;
   return a;
  }
 return fn1;
}
var cf=fn();
cf(50)  ----->这里的结果就是50;这时我们再调用cf();  ----->这时就会返回50,而不是0;

当我们想再次获得a的值时,就可以直接调用cf(),这就实现了在外部获取的里面的值;

注意:闭包函数很消耗内存,所以在退出函数之前,将不使用的局部变量全部删除。

如将当前变量的值设置为“null”,将变量的引用解除,当垃圾回收启动时,会自动对这些值为“null”的变量回收

闭包从编码角度上讲,主要有两种用途

 

  1. 可以读取整个父级作用域函数内部的变量,
  2. 让这些变量的值始终保持在内存中。

构造函数

 用function来创造一类,并且函数名首字母大写,调用构造函数时,必须用关键字new来创建一个实例。例如:

function People(name,age,sex){
  this.name=name,
  this.age=age,
  this.sex=sex
}
var zs=new People("张三",18,"男"}

这里就会打印出:People(name:"张三",age:18,sex:"男"}

 

ES5模拟后端继承实现(不继承原型)

 概念:就是创建一类A后,在另一类B中继承A的属性就是单继承;多继承是再一类C中既继承A类又继承B类,例如:

单继承:
function People(name,age){
  this.name=name;
  this.age=age
}
function Women(name,age,sex){
  People.call(this,name,age)
  this.sex=sex
}
var dc=new Women("貂蝉",20,"女")
 console.log (dc)

这里就会打印出:Women{name:"貂蝉",age:20,sex:"女"}

多继承:
function Man(sex){
   this.sex=sex
}
function Studentman(name,age,sex,job){
  People.call(this,name,age)
  Man.call(this,sex)
  this.job=job
}
var zs=new Studentman("张三",15,"男","学生")
 console.log (zs)

这里就会打印出:Studentman{name:"张三",age:15,sex:"男","学生"}

注意:继承需要修改this的指向(call,apply,bind)

 构造函数与es6中的class

Es5中创建函数的方式与Es6中class

ES5中的构造函数:
function People(name,age,sex){
  this.name=name,
  this.age=age,
  this.sex=sex
}
People.prototype.sayhi=function (){
 console.log("姓名:"+name+",年龄:"+age+",性别:"+sex)
}
var zs=new People("张三",20,"男")
zs.sayhi();
这里显示为:
姓名:张三,年龄:20,性别:男。

ES6中class写法:
class People1{
  constructor(name,age,sex){
    this.name=name,
    this.age=age,
    this.sex=sex
  }
  sayhi=function(){
    console.log("姓名:"+name+",年龄:"+age+",性别:"+sex)
  }
}
var zs=new People1("张三",20,"男")
zs.sayhi();
这里显示结果也为:
姓名:张三,年龄:20,性别:男。

ES5与Es6的对应关系

    1.ES5的构造函数People对应ES6的People类的构造方法constructor;

    2.ES5的People原型上的方法对应Es6的除了constructor以外的其他方法。

通过class实现原型链的继承  

ES5中的写法:
function Fn1(){
  this.fn1="fn1"
}
function Fn2(){
  this.fn2="fn2"
}
function Fn3(){
  this.fn3="fn3"
}
Fn3.prototype.__proto__=new Fn2();
Fn2.prototype.__proto__=new Fn1();
var fn4=new Fn3

console.log(fn4.fn1)
这里显示为:fn1;
ES6中class写法;

class Fn5{
  constructor(){
    this.fn5="fn5";
  }
}
class Fn6 extends Fn5{
  constructor(){
    super();
    this.fn6="fn6";
  }
}
class Fn7 extends Fn6{
  constructor(){
    super();
    this.fn7="fn7";
  }
}
var fn8=new Fn7

console.log(fn8.fn5)
这里显示为:fn5;

 

ES5中的继承原理:

      子类的原型对象的__proto__就是一个父类的实例对象,这样子类实例就能访问父类原型上的方法与属性,父类的原型对象还是Object的一个实例,,所以最终会找到Object的原型对象上去。

ES6中class写法必须有constructor方法,如果你没定义constructor方法,那么最终的构造函数返回的是空对象。

Class的静态方法与静态属性

如果在一个方法前,加上static关键字,就表示该方法是在构造函数本身上定义的方法,不会被实例继承,而是直接通过类来调用,这就称为“静态方法”;而静态属性就跟ES5中一样的格式:构造函数名.属性名=XXXX;例如:

静态方法:
class People{
   constructor(name){
     this.name=name
   }
  static sayhi=function(){
     console.log("我的名字:")
  }
}
这里的sayhi只有被构造函数本身调用,不会被实例继承。
People.sayhi();
结果显示为:我的名字:

静态属性:
var p1=new People
p1.sex="男";
console.log(p1.sex);
结果显示为:男

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值