button执行onclick函数_JS Function 函数

本文详细介绍了JavaScript中的函数,包括函数声明与定义、不同类型的函数(如普通函数、箭头函数、Generator函数和Async函数)、函数调用方式、闭包、函数式编程等概念。重点讨论了函数在提升代码可读性和复用性方面的作用,以及在异步编程中的应用,还特别提到了箭头函数的适用与不适用场景,以及动态创建函数的优缺点。
摘要由CSDN通过智能技术生成

函数

在数学中,函数是一种映射,其功能是将自变量的值 (输入)映射到一个函数值(输出)。例如实数x对应到其平方x2的关系就是一个函数,若以3作为此函数的输入值,所得的输出值便是9。

编程语言中的函数是一段程序代码,其功能是根据输入(参数)进行计算,并产生输出(返回值)。

function square(x) { // 参数就是输入
  return x * x; // 返回值就是输出
}

函数声明

声明函数的存在,包括函数的名字、函数类型以及形参类型。并把这些信息通知编译系统,以便在调用该函数时进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包括函数体。这种情况下函数和函数定义分开。

JS中不存在函数声明。 因为JS是弱类型语言,声明变量的时候并不能确定变量的类型。

函数定义

对函数功能的确立,包括指定函数名,函数类型(返回值)、形参类型(参数)、函数体等,它是一个完整的、独立的函数单位。

  1. 声明式定义

使用关键字function定义函数。

function func() {
  // body
}
  1. 函数表达式定义

在一个表达式中定义一个函数,可以指定函数名也可以不指定。可以理解为定义一个变量,这个变量是函数对象。函数表达式定义的函数在代码执行的时候才会真正创建。

const func = function [name]([param1[, param2[, ..., paramN]]]) { statements };


const func = function() {
  // code
};


const func = function func() {
  // code
}
  1. Function定义

JS支持使用Function / GeneratorFunction动态创建一个新的Function对象,也就是函数对象。

const func = new Function('a', 'b', 'return a + b');
console.log(func(1, 2); // 3

使用Function构造器生成的Function对象是在函数创建时解析的。这比你使用函数声明或者函数表达式(function)并在你的代码中调用更为低效,因为使用后者创建的函数是跟其他代码一起解析的。所以一般情况下不推荐使用这种方式。

虽然性能存在一些问题,但是动态生成函数也有它的优势,可以把复杂的需要动态处理的逻辑动态生成函数使用,比如AJV使用校验规则动态生成具有校验功能的函数对JSON进行校验。

方法

当函数作为对象的属性的时候,我们把函数叫做方法。

class User {
  static staticMethod() {
    console.log('This is a static method');
  }


  instanceMethod() {
    console.log('This is a instance method');
  }
}


const obj = {
  name: 'test',
  method: () {
    console.log('This is a method');
  }
}

为什么要使用函数

  • 减少重复代码
  • 改善程序结构
  • 增强程序的通用性

JS中的函数

JS 所有的函数都是Function()的实例对象(参考JS prototype)。

  • Function:普通函数,使用function定义的函数。
  • Arrow Function:箭头函数,使用=>定义的函数。
  • Generator Function:使用function*定义的函数。
  • Async Function:使用async function定义的函数。

Function

普通函数,也就是我们常说的函数。

Arrow Function

箭头函数表达式的语法比函数表达式更短,简洁。箭头函数更适用于那些本来需要匿名函数的地方。

箭头函数的特点:

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

适用场景

  1. 简化回调函数
const arr = ['a', ' b', ' c '];


// 正常函数写法
arr.map(function (str) {
  return str.trim();
});


// 箭头函数写法
arr.map(str => str.trim())
  1. 预期的this
class UserController {
  async create(user) {
    await model.save(user);
  }
  
  async batchCreate(users) {
    await Promise.all(users.map(user => this.create(user)));
  }
}


const ctrl = new UserController();
await batchCreate([{ name: '1' }, { name: '2' }]);

不适用场景

  1. 定义函数的方法,且该方法内部包括this
const cat = {
  lives: 9,
  jumps: () => { this.lives--; }
};

cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。

  1. 需要动态this
const button = document.getElementById('press'); button.addEventListener('click', () => { this.classList.toggle('on'); });

点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。

Generator Function

generator函数用来返回generator对象,并且它符合可迭代协议和迭代器协议。

在没有async函数之前,generator函数主要用来异步编程。阮一峰老师的ES6入门对generator有详细的有详细的介绍,这里就不再啰嗦。

Async Function

Async函数用来处理异步操作,避免了回调黑洞,让异步代码看起来更human readable。

Async函数的特点

  • 调用async函数的时候会异步执行。
  • 在async函数中使用await的时候,会执行完当前代码才会往下继续执行其他代码,实现按照指定顺序执行异步操作。
  • async函数会返回一个Promise对象。

异常处理

一般async函数配合await实现异步操作的顺序执行。但async函数返回Promise实例对象,所以async函数可以像Promise一样使用。所以有两种处理方式处理async函数异常。

  1. try catch
async function fun() {
  await Promise.reject('error test')
}


async function run() {
  try {
    await fun();
  } catch(e) {
    console.log(e); // catch error test
  }
}


run()
  1. catch()
async function fun() {
  await Promise.reject('error test')
}


fun()
.then(v => console.log('success'))
.catch(e => console.log('catch', e)); // catch error test

参数

参数类型

  1. 形参

形式参数,是在声明或定义函数的时候在()中使用的参数,目的是用来接收调用该函数时传递的参数。可以把形参理解为一种变量。

形参只有在函数被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。

  1. 实参

实际参数,是在调用函数时传递的参数,即传递给被调用函数的值。

参数传递

  • 值传递:对于基本数据类型来说,形参会拷贝实参的值(参考JS Data & Type基本类型)。
  • 引用传递:对于引用类型来说,形参拷贝实参的内存地址(参考JS Data & Type引用类型)。

静态化

JS Data & Type中提过动态类型静态化这个概念,在这里也提出参数静态化:1.参数类型静态化;2.参数个数静态化。

  1. 参数类型静态化

也就是参数的类型需要动态类型静态化。JS Data & Type函数参数部分。

  1. 参数个数静态化

JS函数支持不写形参,在调用函数的时候可以传递任意的实参,在函数体中直接arguments这样的变量就可以获得传进来的参数。这样容易造成阅读和使用上的误解,需要在函数体代码中寻找理解。所以尽量把参数全部写到形参列表中,明确函数都有哪些形参。

函数调用

JS有三种函数调用方式:直接调用、apply调用和call调用。

不过个人不太推荐apply和call这两种方式,因为这两种方式有时候让代码理解起来困惑。所以为了保持代码的简洁性和清晰性,除非是不得不用,否则使用直接调用的方式。

直接调用

funcName(arg1, arg2, ..) 函数名 + 参数。

Apply

通过apply方法可以调用它所属的函数(对象)。可以给函数提供运行时的this值,以及使用一个数组(或类似数组对象)作为参数。

func.apply(thisArg, [argsArray])

  • thisArg:可选。在 func 函数运行时使用的 this 值。
  • argsArray:可选。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。
const obj = {
  name : 'apply'
}


function func(firstName, lastName){
  console.log(`${firstName} ${this.name} {lastName}`);
}


func.apply(obj, ['weber', 'yang']); // weber apply yan

Call

通过call方法可以调用它所属的函数(对象)。可以给函数提供运行时的this值,以及逐个提供各个参数(参数列表)。

fun.call(thisArg, arg1, arg2, ...)

  • thisArg:在fun函数运行时指定的this值。
  • arg1, arg2, ...:参数列表
const obj = {
  name: 'call'
}


function func(firstName, lastName) {
  console.log(`${firstName} ${this.name} {lastName}`);
}


func.call(obj, 'weber', 'yang'); // weber apply yan

Apply & Call

apply和call都是函数对象的方法,两者都可以改变函数运行时的this,这个是apply和call的主要使用的功能。

apply和call不同在于,提供的参数格式不一样:apply需要的是一个参数数组;call需要的是参数列表。

co是一个generator流程控制器。在有async函数之前,co和generator是绝佳搭配。co代码小巧精悍,值得学习其中的JS技巧。

co.wrap用来封装generator成Promise对象,使得generator可以像Promise对象一样使用,同时也依然保持generator要完成的功能,也就是generator会依然正常执行。

co.wrap = function (fn) {
  createPromise.__generatorFunction__ = fn;
  return createPromise;
  function createPromise() {
    return co.call(this, fn.apply(this, arguments));
  }
};


function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1);
 
  return new Promise(function(resolve, reject) {
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);
    ...
  });
}


co.wrap(function* (arg) {
})('arg value').then(function (value) {
})

wrap返回了闭包,闭包包含了对fn封装的代码。通过往闭包传入参数使得fn封装后依然可以接收参数进行调用。通过co.call注入了this,把fn封装到了Promise对象。通过fn.apply把this注入到fn;使用fn.apply而不是fn.call的原因是,传入的参数是不确定的,arguments类数组可以拿到传入的参数列表,所以用apply。

gen.apply(ctx, args)使用apply的原因是参数不确定,所以使用了slice.call(arguments, 1);通过ctx保持原来的this不变。

this 指向问题

0ea5f692c3be0152b395b4cbbcc3e4a2.png

176106a1fedd0342232c93dfb87f746e.png

Bind

bind方法创建并返回一个新的函数,当这个绑定函数被调用时this值为其提供的值,其参数列表前几项值为创建绑定函数时指定的参数序列。

fun.bind(thisArg[, arg1[, arg2[, ...]]])

  • thisArg:调用绑定函数时作为this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。
  • arg1, arg2, ...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

bind与apply和call不同的是:apply和call是在每次调用的时候动态指定被调用函数的this和实参,apply和call自动帮我们对目标函数进行调用;而bind是创建一个新的封装绑定函数,这个绑定函数固定了目标函数的this值,和部分实参。

为了使得代码逻辑更加的清晰,controller层会使用class面向对象组织代码。一般把controller通用的代码逻辑放在Base Controller中,其他Controller通过继承Base,在Controller实例中调用this,就可以访问这些方法了。但是问题来了,this是动态确定的,当Controller实例的方法和router绑定后,由router调用Controller实例的方法的时候,this就改变了,这样就无法访问其他实例方法了。这个时候可以考虑使用bind绑定Controller实例,这样Controller实例是方法就可以通过this访问其他实例方法了。

// /controllers/base
class Base {
  // 权限校验
  assertScope(ctx, role = 'user') {
    ctx.assert(ctx.state.me && ctx.state.me.role === role, 401);
  }
}


module.exports = Base;


// /controllers/order
class Order extends Base {
  async find() {
    this.assertScope(ctx);
    // other code
  }
}


module.exports = new Order();


// /routers/
const KoaRouter = require('koa-router');
const ctrl = require('../controllers/order');
const router = KoaRouter({ prefix: '/api' });


router.post('order.find', '/orders', ctrl.find.bind(ctr

IIFE

立即调用函数表达式,是一个在定义时就会立即执行的 JS 函数。

// 支持所有类型的函数
(function () {
  // body
})();

第一部分是包围在圆括号运算符() 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。 第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。

const result = (function () {
  return 'return value';
})();
// IIFE 执行后返回的结果 'return value'

函数原型链

JS prototype

闭包

JS Scope & Closure

函数上下文

JS Execution Context

函数式编程

函数式编程是编程范式中的一种,是一种典型的编程思想和方法。其他的编程范式还包括面向对象编程,逻辑编程等。

编程范式的意义在于它提供了模块化代码的各种思想和方法。

  • 模块化使得开发更快、维护更容易
  • 模块可以复用
  • 模块化便于单元测试和debug

由此可以理解为:函数式编程是以函数为核心来组织模块的一套编程方法。(不是写了函数就是函数式编程)

高阶函数 Higher-order function

满足一个或多个条件的函数属于高阶函数:

  • 接受一个或多个函数作为输入
  • 输出一个函数

函数参数

Array.prototype.sort

// 从小到大排列
[1, 3, 2].sort((a, b) => a - b);


//从大到小排列
[1, 3, 2].sort((a, b) => b - a);

函数返回值

函数作为返回值一个典型的情况就是闭包。比如之前提到的co.wrap使用了闭包。

// koa-bodyparser
co.wrap = function (fn) {
  createPromise.__generatorFunction__ = fn;
  return createPromise;
  function createPromise() {
    return co.call(this, fn.apply(this, arguments));
  }
};

纯函数

符合以下要求的函数就是纯函数:

  • 相同输入总是会返回相同的输出。返回的结果只依赖于输入的参数且与外部系统状态无关。
  • 没有副作用。不会影响该函数作用域以外的外部状态(比如全局变量、参数)。

优点:

  • 更加容易被测试,因为它们唯一的职责就是根据输入计算输出。
  • 结果可以被缓存,因为相同的输入总会获得相同的输出。
  • 自我文档化,因为函数的依赖关系很清晰。
  • 更容易被调用,因为你不用担心函数会有什么副作用。

柯里化

把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个新的函数的技术,新函数接受余下参数并返回运算结果。

参考

  • 程序设计思想与方法
  • ECMAScript6入门
  • 6 ways to declare JavaScript functions
  • Exploring ES6
  • JS 函数式编程指南
  • JavaScript专题之函数柯里化
在 `button` 标签的 `onclick` 属性引用 Java 函数,需要使用 JSP 或者 Servlet 来实现。 以下是一个简单的示例: 在 JSP 页面: ```html <%@ page import="com.example.MyClass" %> <!-- 引入你的 Java 类 --> <!DOCTYPE html> <html> <head> <title>Example</title> </head> <body> <button onclick="myFunction()">点击按钮</button> <script> function myFunction() { <% MyClass myClass = new MyClass(); %> <!-- 创建 MyClass 的实例 --> <% myClass.myMethod(); %> <!-- 调用 MyClass 的 myMethod() 方法 --> } </script> </body> </html> ``` 在 Servlet : ```java package com.example; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 处理 GET 请求 } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 处理 POST 请求 } public void myMethod() { // 在 Java 编写你的方法逻辑 } } ``` 在 JSP 页面引用 Servlet 类: ```html <!DOCTYPE html> <html> <head> <title>Example</title> </head> <body> <button onclick="myFunction()">点击按钮</button> <script> function myFunction() { var xhr = new XMLHttpRequest(); xhr.open("GET", "MyServlet", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); } }; xhr.send(); } </script> </body> </html> ``` 在这个例子,我们在 JSP 页面引入了 `MyClass` 类,并在 `myFunction()` 函数创建了 `MyClass` 的实例,并调用了 `myMethod()` 方法。在 Servlet ,我们编写了 `myMethod()` 方法的逻辑,在 JSP 页面可以通过 Ajax 发送请求到 Servlet,从而调用 `myMethod()` 方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值