当前位置: 首页 > 编程日记 > 正文

javascript函数全解

0.0 概述

本文总结了js中函数相关的大部分用法,对函数用法不是特别清晰的同学可以了解一下。

1.0 简介

同其他语言不同的是,js中的函数有2种含义。

普通函数:同其他语言的函数一样,是用于封装语句块,执行多行语句的语法结构。

构造函数:不要把它当作函数,把它当作class,内部可以使用this表示当前对象。

【注】后续代码基于ES6&ES7标准,笔者是在nodejs v10.7.0环境下运行(你也可以选择其他支持ES6的node版本)。

1.1 函数的声明

虽然普通函数和构造函数,含义有所不同,可是声明方法却完全一样。

1.1.0 函数声明

function sort(arr) {let ret = [...arr];let length = ret.length;for (let i = 0; i < length; i++) {for (let j = i + 1; j < length; j++) {if (ret[i] > ret[j]) {[ret[j], ret[i]] = [ret[i], ret[j]];}}}return ret;
}

1.1.1 函数表达式

let sort = function (arr) {let ret = [...arr];......return ret;
}

函数表达式和普通函数声明的区别在于,普通函数声明会提升,函数表达式不会提升

“提升”的意思是说: 在函数声明前就可以调用这个函数。不必先声明后调用。

js会在运行时,将文件内所有的函数声明,都提升到文件最顶部,这样你可以在代码任意位置访问这个函数。

而现在根据ES6标准,使用var修饰的函数表达式会提升,使用let修饰的则不会提升。

1.1.2 使用Function构造函数声明

let sort = new Function("arr", `function sort(arr) {let ret = [...arr];let length = ret.length;for (let i = 0; i < length; i++) {for (let j = i + 1; j < length; j++) {if (ret[i] > ret[j]) {[ret[j], ret[i]] = [ret[i], ret[j]];}}}return ret;}`);

这种使用Function构造方法创建的函数,同函数声明产生的函数是完全相同的。

构造函数接收多个字符串作为参数,最后一个参数表示函数体,其他参数表示参数名

像上面这个例子和1.1.0中的声明完全相同。

这种声明方式,没有发现有什么优点,并不推荐使用。

1.2 闭包

闭包,简单说就是在函数中声明的函数,也就是嵌套函数。它能够延长父作用域部分变量的生命周期。

闭包可以直接使用其所在函数的任何变量,这种使用是引用传递,而不是值传递,这一点很重要。

let f = function generator() {let arr = [1, 2, 3, 4, 5, 6, 7];let idx = 0;return {next() {if (idx >= arr.length) {return { done: true };} else {return { done: false, value: arr[idx++] };}}}
}
let gen = f();
for (let i = 0; i < 10; i++) {console.log(gen.next());
}

上面的代码中,generator函数中的闭包next()可直接访问并修改所在函数中的变量arridx

一般说来,闭包需要实现尾递归优化。

尾递归是指,如果一个函数,它的最后一行代码是一个闭包的时候,会在函数返回时,释放父函数的栈空间。

这样一来,依赖闭包的递归函数就不怕栈溢出了(nodejs在64位机器上可达到1万多层的递归才会溢出,有可能是根据内存情况动态计算的)。

ES6明确要求支持尾递归。

而据网络上资料说,nodejs需要在严格模式下,使用–harmony选项,可以开启尾递归。

然而我使用下列代码发现,并没有开启(nodejs版本为v10.3.0)。


// File: test.js
// Run: node --harmony test.js"use strict"function add(n, sum) {if (n == 0) {console.trace();return sum;} else {return add(n - 1, sum + n);}
}
console.log(add(10, 0));
/*
输出为:
Traceat add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:5:11)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)at add (/Users/hongyuwang/Desktop/javascript/learn/learn.js:8:10)
55
*/

1.3 匿名函数

我们经常在js的代码中看见下面这种写法:

(function(){.........
})();

将一个匿名函数直接执行,如果刚接触js的同学可能觉得这是脱裤子放屁。

但是这个匿名函数的最大作用在于作用域隔离,不污染全局作用域。

如果没有匿名函数包裹,代码中声明的所有变量都会出现在全局作用域中,造成不必要的变量覆盖麻烦和性能上的损失。

ES6中这种写法可以抛弃了,因为ES6引入了块作用域

{.........
}

作用和上面的匿名函数相同。

另外ES6中增加了一种匿名函数的写法:

//ES6以前的写法
function Teacher(name){this.name = name;var self = this;setTimeout(function(){console.log('Teacher.name = ' + self.name);}, 3000);
}//现在这样写
function Student(name){this.name = name;setTimeout(() => {console.log('Student.name = ' + this.name);}, 3000);
}

新的匿名函数的在写法上有2处不同:

  • 去掉了function关键字
  • 在参数列表和函数体之间增加了=>符号

而它也带来了一个巨大的好处:

匿名函数中的this对象总是指向声明时所在的作用域的this,不再指向调用时候的this对象了。

这样我们就可以像上面的例子那样,很直观地使用this,不用担心出现任何问题。

所以比较强烈推荐使用新的匿名函数写法。

1.4 构造函数和this

1.4.1 基本面向对象语法

下面来介绍构造函数,js没有传统面向对象的语法,但是它可以使用函数来模拟。

了解js面向对象机制之前,可以先看一下,其他标准面向对象语言的写法,比如java,我们声明一个类。

class Person{//构造函数Person(String name, int age){this.name = name;this.age = age;Person.count++;}//属性String name;int age;//setter&getter方法String getName(){return this.name;}void setName(String name){this.name = name;}int getAge(){return this.age;}void setAge(int age){this.age = age;}//静态变量static int count = 0;//静态方法public int getInstanceCount(){return Person.count;}
}

由此可知,一个类主要包含如下元素:构造函数属性方法静态属性静态方法

在js中,我们可以使用js的构造函数,来完成js中的面向对象。

js的构造函数就是用来做面向对象声明(声明)的。

构造函数的声明语法同普通函数完全相同。

//构造函数
function Person(name, age){//属性this.name = name;this.age = age;//setter&getterthis.getName = function(){return this.name;}this.setName = function(name){this.name = name;}this.getAge = function(){return this.age;}this.setAge = function(age){this.age = age;}Person.count++;
}//静态变量
Person.count = 0;//静态方法
Person.getInstanceCount = function(){return Person.count;
}

可以发现,构造函数中同普通函数相比,特别的地方在于使用了this,同其他面向对象的语言一样,this表示当前的实例对象。

把我们用js声明的类与java的类相对比,二者除了写法不同之外,上述关键元素也都包含了。

1.4.2 prototype

js使用上面的方法声明了类之后,就可以使用new关键字来创建对象了。

let person = new Person("kaso", 20);
console.log("person.name=" + person.getName() + ", person.age=" + person.getAge());
//输出:person.name=kaso, person.age=20
let person1 = new Person("jason", 25);
console.log("person.name=" + person.getName() + ", person.age=" + person.getAge());
//输出:person.name=jason, person.age=25

创建对象,访问属性,访问方法,都没问题,看起来挺好的。

但是当我们执行一下这段代码,会发现有些不对:

console.log(person.getName === person1.getName);
//输出:false

原来构造函数在执行的时候,会将所有成员方法,为每个对象生成一份copy,而对于类成员函数来说,保留一份copy就足够了,而不同的对象可以用this来区分。上面的做法很明显,内存被白白消耗了。

基于上述问题,js引入了prototype关键字并规定:

存储在prototype中的方法和变量可以在类的所有对象中共享。

因此,上面的构造函数可以修改成这样:

function Person(name, age){this.name = name;this.age = age;Person.count++;
}Person.prototype.getName = function(){return this.name;
}Person.prototype.setName = function(name){this.name = name;
}Person.prototype.getAge = function(){return this.age;
}Person.prototype.setAge = function(age){this.age = age;
}Person.count = 0;Person.getInstanceCount = function(){return Person.count;
}

运行效果和之前的写法相同,只是这次创建不同的对象时,成员方法不再创建多个副本了。

需要注意的是,成员变量不需要放到prototype中,可以想想为什么。

1.4.3 apply和call

js函数中绕不过的一个问题就是,方法里面的this到底指向哪里?

最官方的说法是:this指向调用此方法的对象。

对于类似于java这种面向对象的语言来讲,this永远指向所在类的对象实例。

对于js中也是这样,如果我们规规矩矩地像上一节介绍的那样使用,this也会指向所在类的对象实例。

但是,js也提供了更为灵活的语法,它可以让一个方法被不同的对象调用,即使不是同一个类的对象,也就是可以将同一个函数的this,设为不同的值。

这是一个极为灵活的语法,可以完成其他语言类似接口(interface)扩展(extension)模版(template)的功能。

实现此功能的方法有2个:applycall,二者实现的功能完全相同,即改变函数的this指向,只是函数传递参数方式不同。

call接受可变参数,同函数调用一样,需将参数一一列出。
apply只接受2个参数,第一个就是新的this指向的对象,第二个参数是原参数用数组保存起来。
代码如下:

let obj = {print(a, b, c){console.log(`this is obj.print(${a}, ${b}, ${c})`);}
}let obj1 = {print(a, b, c){console.log(`this is obj1.print(${a}, ${b}, ${c})`);}
}function test(a, b, c){this.print(a, b, c);
}test.apply(obj, [1, 2, 3]);
test.call(obj, 4, 5, 7);test.apply(obj1, [1, 2, 3]);
test.call(obj1, 4, 5, 7);/* 输出:
this is obj.print(1, 2, 3)
this is obj.print(4, 5, 7)
this is obj1.print(1, 2, 3)
this is obj1.print(4, 5, 7)
*/

1.4.4 继承

面向对象3大特征:封装,继承,多态,其中最重要的就是继承,多态也依赖于继承的实现。可以说实现了继承,就实现了面向对象。

java中的继承很简单:

class Student extends Person{... ...
}

Student继承之后自动获得Person的所有成员变量和成员方法。

因此,我们在实现js继承的时候,主要就是获取到父类的成员变量和成员方法。

最简单的实现就是,将父类的成员变量和方法直接copy到子类中。

这需要做2件事:

  • 为了copy成员方法,可以将Student的prototype指向父类的prototype
  • 为了copy成员属性,子类构造函数需要调用父类构造函数
function Student(name, age){Person.call(self, name, age);
}Student.prototype = Person.prototype;

上面代码可以达到继承的目的,但是会产生两个问题

  • 如果我向Student中添加新的成员方法时,会同时加入到父类中
  • 多层次继承无法实现,即当所调用的方法在父类中找不到的时候,不会去父类的父类中去查找

所以我们不能直接将Person.prototype直接给Student.prototype。

经过思考,一个可行方案是,令子类prototype指向父类的一个对象,即像这样:

Student.prototype = new Person();

这样做,可以解决上面的2个问题。

但是它仍然有些瑕疵:会调用2次父类构造函数,造成一定的性能损失。

所以我们的终极继承方案是这样的:

function Student(name, age){Person.call(self, name, age);
}function HelpClass(){}
HelpClass.prototype = Person.prototype;
Student.prototype = new HelpClass();

上面关键代码的意义在于,用一个空的构造函数代替父类构造函数,这样调用了一个空构造函数的代价会小于调用父类构造函数。

另外上述代码可以用Object.create函数简化:

function Student(name, age){Person.call(self, name, age);
}Student.prototype = Object.create(Person.prototype);

这就是我们最终的继承方案了。可以写成下面的通用模式。

function extend(superClass){function subClass(){superClass.apply(self, arguments);}subClass.prototype = Object.create(superClass.prototype);return subClass;
}let Student = extend(Person);let s = new Student('jackson', '34');console.log("s.getName() = " + s.getName() + ", s.getAge() = " + s.getAge());//输出为:s.getName() = jackson, s.getAge() = 34

当然实现一个完整的继承还需要完善其他诸多功能,在这里我们已经解决了最根本的问题。

1.5 generator函数和co

generator是ES6中提供的一种异步编程的方案。有点像其他语言(lua, c#)中的协程。

它可以让程序在不同函数中跳转,并传递数据。

1.5.1 基本用法介绍

看下面的代码:

function *generatorFunc(){console.log("before yield 1");yield 1;console.log("before yield 2");yield 2;console.log("before yield 3");let nextTransferValue = yield 3;console.log("nextTransferValue = " + nextTransferValue);
}let g = generatorFunc();
console.log("before next()");
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next(1024));
/*输出:
before next()
before yield 1
{ value: 1, done: false }
before yield 2
{ value: 2, done: false }
before yield 3
{ value: 3, done: false }
nextTransferValue = 1024
{ value: undefined, done: true }
*/

可以看到generator函数有3要素:

  • 需要在函数名字前面,加上*
  • 需要在函数体中使用 yield
  • 调用的时候需要使用 next()函数

另外还有一些其他规则:

  • generator函数内的第一行代码,需要在第一个next()执行后执行
  • 函数在执行next()时,停顿在yield处,并返回yield后面的值,yield后的代码不再执行。
  • next() 返回的形式是一个对象:{value: XXX, done: false},这个对象中,value表示yield后面的值,done表示是否generator函数已经执行完毕,即所有的yield都执行过了。
  • next() 可以带参数,表示将此参数传递给上一个yield,因为上次执行next()的时候,代码停留在上次yield的位置了,再执行next()的时候,会从上次yield的位置继续执行代码,同时可以令yield表达式有返回值。

从上述介绍中可以看出,generator除了在函数中跳转之外,还可以通过next()来返回不同的值。

了解过ES6的同学应该知道,这种next()序列,特别符合迭代器的定义。

因此,我们可以很容易把generator的函数的返回值组装成数组,还可以用for..of表达式来遍历。

function *generatorFunc(){yield 1;yield 2;yield 3;
}let g = generatorFunc();
for(let i of g){console.log(i);
}/*
输出:
1
2
3
*/
function *generatorFunc(){yield 1;yield 2;yield 3;
}let g = generatorFunc();
console.log(Array.from(g));/*
输出:
[1, 2, 3]
*/

除了上述规则外,generator还有一个语法yield *,它可以连接另一个generator函数,类似于普通函数间调用。用于一个generator函数调用另一个generator函数,也可用于递归。

function *generatorFunc(){yield 3;yield 4;yield 5;
}function *generatorFunc1(){yield 1;yield 2;yield * generatorFunc();yield 6;
}let g = generatorFunc1();
console.log(Array.from(g));/*
输出:
[1, 2, 3, 4, 5, 6]
*/

除了获取数组外,我们还可以使用generator的yieldnext特性,来做异步操作。

js中的异步操作我们一般使用Promise来实现。

请看下列代码及注释。

let g = null;
function *generatorFunc(){//第一个请求,模拟3s后台操作let request1Data = yield new Promise((resolve, reject) => {setTimeout(()=>{resolve("123");}, 3000);}).then((d) => {//令函数继续运行,并把promise返回的数据通过next传给上一个yield,代码会运行到下一个yieldg.next(d);});//输出第一个请求的结果console.log('request1Data = ' + request1Data);//同上,开始第二个请求let request2Data = yield new Promise((resolve, reject) => {setTimeout(()=>{resolve("456");}, 3000);}).then((d) => {g.next(d);});//第二个请求console.log('request2Data = ' + request2Data);}g = generatorFunc();g.next();console.log('completed');/*输出:completed(马上输出)request1Data = 123(3s后输出)request2Data = 456(6s后输出)*/

我们换一种写法:

let g = null;function *request1(){return yield new Promise((resolve, reject) => {setTimeout(()=>{resolve("123");}, 3000);}).then((d) => {g.next(d);});
}function *request2(){return yield new Promise((resolve, reject) => {setTimeout(()=>{resolve("456");}, 3000);}).then((d) => {g.next(d);});
}function *generatorFunc(){let request1Data = yield *request1();console.log('request1Data = ' + request1Data);let request2Data = yield *request2();console.log('request2Data = ' + request2Data);}g = generatorFunc();g.next();console.log('completed');/*输出同上*/

运行结果是相同的,所以我们可以看到,generator函数能够把异步操作写成同步形式,从而避免了回调地狱的问题。

异步变成同步,不知道能够避免多少因为回调,作用域产生的问题,代码逻辑也能急剧简化。

1.5.2 generator函数的自动运行

虽然我们可以通过generator消除异步代码,但是使用起来还是不太方便的。

需要把generator对象提前声明保存,然后还要在异步的结果处写next()

经过观察发现,这些方法的出现都是有规律的,所以可以通过代码封装来将这些操作封装起来,从而让generator函数的运行,就像普通函数一样。

提供这样功能的是co.js(可以点这里跳转),大神写的插件,用于generator函数的自动运行,简单的说它会帮你自动执行next()函数,所以借助co.js,你只需要编写yield和异步函数即可。

使用co.js,上面的异步代码可以写成这样:

let co = require('./co');function *request1(){return yield new Promise((resolve, reject) => {setTimeout(()=>{resolve("123");}, 3000);});
}function *request2(){return yield new Promise((resolve, reject) => {setTimeout(()=>{resolve("456");}, 3000);});
}function *generatorFunc(){let request1Data = yield *request1();console.log('request1Data = ' + request1Data);let request2Data = yield *request2();console.log('request2Data = ' + request2Data);}co(generatorFunc);console.log('completed');/*输出同上*/

可以看到,借助co.js你只需要写yield就能够把异步操作写成同步调用的形式。

注意,请使用promise来进行异步操作。

1.6 async和await

使用generator + Promise + co.js可以较为方便地实现异步转同步。

而js的新标准中,上面的操作已经提供了语法层面的支持,并将异步转同步的写法,简化成了2个关键字:awaitasync

同样实现上节中的异步调用功能,代码如下:


async function request1(){return await new Promise((resolve, reject) => {setTimeout(()=>{resolve("123");}, 3000);});
}async function request2(){return await new Promise((resolve, reject) => {setTimeout(()=>{resolve("456");}, 3000);});
}async function generatorFunc(){let request1Data = await request1();console.log('request1Data = ' + request1Data);let request2Data = await request2();console.log('request2Data = ' + request2Data);}generatorFunc();console.log('completed');/*输出同上*/

await/async使用规则如下:

  • await只能用在async函数中。
  • await后面可以接任何对象。
  • 如果await后面接的是普通对象(非Promise,非async),则会马上返回,相当于没写await。
  • 如果await后面是Promise对象,await会等待Promise的resolve执行后,才会继续向下执行,然后await会返回resolve传递的参数。
  • 如果await后面是另一个async函数,则会等待另一个async完成后继续执行。
  • 调用一个async函数会返回一个Promise对象,async函数中的返回值相当于调用了Promise的resolve方法,async函数中抛出异常相当于调用了Promise的reject方法。
  • 通过上一条规则可知,虽然await/async使用了Promise来执行异步,但是我们却可以在使用这两个个关键字的时候,不写任何的Promise。
  • 另外,如果await后面的表达式可能抛出异常,则需要在await语句上增加try-catch语句,否则异常会导致程序执行中断。

await/async本身就是用来做异步操作转同步写法的,它的规则和用法也很明确,只要牢记上面几点,你就能用好它们。


//抛出异常的async方法
async function generatorFunc1(){console.log("begin generatorFunc1");throw 1001;
}//async方法返回的是Promise对象,使用Promise.catch捕获异常
generatorFunc1().catch((e) => {console.log(`catch error '${e}' in Promise.catch`);
})//正常带返回值的async方法
async function generatorFunc2(){console.log("begin generatorFunc2");return 1002;
}//async方法返回的是Promise对象,使用Promise.then获取返回的数据
generatorFunc2().then((data)=>{console.log(`data = ${data}`);
})//await后带的async方法若抛出异常,可以在await语句增加try-catch捕获异常
async function generatorFunc3(){console.log("begin generatorFunc3");try{await generatorFunc1();}catch(e){console.log(`catch error '${e}' in generatorFunc3`);}
}generatorFunc3();console.log('completed');
/* 输出:
begin generatorFunc1
begin generatorFunc2
begin generatorFunc3
begin generatorFunc1
completed
catch error '1001' in Promise.catch
data = 1002
catch error '1001' in generatorFunc3
*/

–完--

相关文章:

MYSQL explain详解[转载]

explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。 虽然这篇文章我写的很长&#xff0c;但看起来真的不会困啊&#xff0c;真的都是干货啊&#xff01;&#xff01;&#xff01;&#xff01; 先解析一条sql语句&…

CodeForces 157A Game Outcome

A. Game Outcometime limit per test2 secondsmemory limit per test256 megabytesinputstandard inputoutputstandard outputSherlock Holmes and Dr. Watson played some game on a checkered board n  n in size. During the game they put numbers on the boards squares…

我使用Python和Django在自己的网站上建立了一个会员专区。 这是我学到的东西。

I decided it was time to upgrade my personal website in order to allow visitors to buy and access my courses through a new portal. 我认为是时候升级我的个人网站了&#xff0c;以允许访问者通过新的门户购买和访问我的课程 。 Specifically, I wanted a place for v…

详解AFNetworking的HTTPS模块

0.0 简述 文章内容包括&#xff1a; AFNetworking简介ATS和HTTPS介绍AF中的证书验证介绍如何创建服务端和客户端自签名证书如何创建简单的https服务器对CA正式证书和自签名证书的各种情况进行代码验证 文中所涉及的文件和脚本代码请看这里。 1.0 AFNetworking简介 AFNetwo…

字符串专题:map POJ 1002

第一次用到是在‘校内赛总结’扫地那道题里面&#xff0c;大同小异 map<string,int>str 可以专用做做字符串的匹配之类的处理 string donser; str [donser] 自动存donser到map并且值加一&#xff0c;如果发现重复元素不新建直接加一&#xff0c; map第一个参数是key&…

【洛谷P1508】吃吃吃

题目背景 问世间&#xff0c;青春期为何物&#xff1f; 答曰&#xff1a;“甲亢&#xff0c;甲亢&#xff0c;再甲亢&#xff1b;挨饿&#xff0c;挨饿&#xff0c;再挨饿&#xff01;” 题目描述 正处在某一特定时期之中的李大水牛由于消化系统比较发达&#xff0c;最近一直处…

前端和后端开发人员比例_前端开发人员vs后端开发人员–实践中的定义和含义

前端和后端开发人员比例Websites and applications are complex! Buttons and images are just the tip of the iceberg. With this kind of complexity, you need people to manage it, but which parts are the front end developers and back end developers responsible fo…

Linux 创建子进程执行任务

Linux 操作系统紧紧依赖进程创建来满足用户的需求。例如&#xff0c;只要用户输入一条命令&#xff0c;shell 进程就创建一个新进程&#xff0c;新进程运行 shell 的另一个拷贝并执行用户输入的命令。Linux 系统中通过 fork/vfork 系统调用来创建新进程。本文将介绍如何使用 fo…

metasploit-smb扫描获取系统信息

1.msfconsle 2.use auxiliary/scanner/smb/smb_version 3. msf auxiliary(smb_version) > set RHOSTS 172.16.62.1-200RHOSTS > 172.16.62.1-200msf auxiliary(smb_version) > set THREADS 100THREADS > 100msf auxiliary(smb_version) > run 4.扫描结果&#x…

算法(1)斐波那契数列

1.0 问题描述 实现斐波那契数列&#xff0c;求第N项的值 2.0 问题分析 斐波那契数列最简单的方法是使用递归&#xff0c;递归和查表法同时使用&#xff0c;可以降低复杂度。根据数列特点&#xff0c;同时进行计算的数值其实只有3个&#xff0c;所以可以使用3个变量循环递进计…

主键SQL教程–如何在数据库中定义主键

Every great story starts with an identity crisis. Luke, the great Jedi Master, begins unsure - "Who am I?" - and how could I be anyone important? It takes Yoda, the one with the Force, to teach him how to harness his powers.每个伟大的故事都始于…

算法(2)KMP算法

1.0 问题描述 实现KMP算法查找字符串。 2.0 问题分析 “KMP算法”是对字符串查找“简单算法”的优化。字符串查找“简单算法”是源字符串每个字符分别使用匹配串进行匹配&#xff0c;一旦失配&#xff0c;模式串下标归0&#xff0c;源字符串下标加1。可以很容易计算字符串查…

告别无止境的增删改查:Java代码生成器

对于一个比较大的业务系统&#xff0c;我们总是无止境的增加&#xff0c;删除&#xff0c;修改&#xff0c;粘贴&#xff0c;复制&#xff0c;想想总让人产生一种抗拒的心里。那有什么办法可以在正常的开发进度下自动生成一些类&#xff0c;配置文件&#xff0c;或者接口呢&…

Maven国内源设置 - OSChina国内源失效了,别更新了

Maven国内源设置 - OSChina国内源失效了&#xff0c;别更新了 原文&#xff1a;http://blog.csdn.net/chwshuang/article/details/52198932 最近在写一个Spring4.x SpringMVCMybatis零配置的文章&#xff0c;使用的源配的是公司的私有仓库&#xff0c;但是为了让其他人能够通过…

如何使用Next.js创建动态的Rick and Morty Wiki Web App

Building web apps with dynamic APIs and server side rendering are a way to give people a great experience both with content and speed. How can we use Next.js to easily build those apps?使用动态API和服务器端渲染来构建Web应用程序是一种使人们在内容和速度上都…

安装部署Spark 1.x Standalone模式集群

Configuration spark-env.sh HADOOP_CONF_DIR/opt/data02/hadoop-2.6.0-cdh5.4.0/etc/hadoop JAVA_HOME/opt/modules/jdk1.7.0_67 SCALA_HOME/opt/modules/scala-2.10.4 ####################################################### #主节点 …

算法(3)简单四则运算

1.0 问题描述 实现10以内四则运算&#xff08;只包含数字&#xff0c;*/和小括号&#xff09; 2.0 问题分析 四则运算使用“后缀表达式”算法来计算&#xff0c;后缀表达式可以无需考虑运算符优先级&#xff0c;直接从左至右依次计算。问题分解成2部分&#xff0c;一是将“中…

调用短信接口,先var_dump()看数据类型是object需要json_decode(json_encode( $resp),true)转换成array...

返回的数据.先看类型,如果是object类型 先json_encode, 再json_decode,加true 转换成数组 $resp $c->execute($req); var_dump($resp); object(stdClass)#12 (2) { ["result"]> object(stdClass)#13 (3) { ["err_code"]> string(1) "0"…

nlp文本数据增强_如何使用Texthero为您的NLP项目准备基于文本的数据集

nlp文本数据增强Natural Language Processing (NLP) is one of the most important fields of study and research in today’s world. It has many applications in the business sector such as chatbots, sentiment analysis, and document classification.Preprocessing an…

R语言-基础解析

二、操作基础%%取余%/%整数除法(1)eigen(...)求解方阵的特征值和特征向量(2)solve(D,A)求解DXA(3)data<-list(...)取里面的对象data[["列名称"]]&#xff1b;data[[下标]]&#xff1b;data$列名称(4)unlist(列表对象)把列表对象转化为向量对象(5)names(数据框)读取…

算法(4)数据结构:堆

1.0 问题描述 实现数据结构&#xff1a;堆。 2.0 问题分析 堆一般使用数组来表示&#xff0c;其中某个节点下标i的两个子节点的下标为 2i1 和 2i2。堆是一棵完全二叉树。堆有3种基本操作&#xff1a;创建&#xff0c;插入&#xff0c;删除。这3种操作都需要通过“调整堆”的…

cookie 和session 的区别详解

转自 https://www.cnblogs.com/shiyangxt/archive/2008/10/07/1305506.html 这些都是基础知识&#xff0c;不过有必要做深入了解。先简单介绍一下。 二者的定义&#xff1a; 当你在浏览网站的时候&#xff0c;WEB 服务器会先送一小小资料放在你的计算机上&#xff0c;Cookie 会…

如何设置Java Spring Boot JWT授权和认证

In the past month, I had a chance to implement JWT auth for a side project. I have previously worked with JWT in Ruby on Rails, but this was my first time in Spring. 在过去的一个月中&#xff0c;我有机会为辅助项目实现JWT auth。 我以前曾在Ruby on Rails中使用…

算法(5)哈希表

1.0 问题描述 实现数据结构&#xff1a;哈希表。 2.0 问题分析 哈希表可以看作我们经常使用的字典&#xff08;swift&#xff09;或对象&#xff08;js&#xff09;&#xff0c;可以让一个key&value对一一对应&#xff0c;可以快速根据key找到value。哈希表内部使用数组…

《面向对象程序设计》c++第五次作业___calculator plus plus

c第五次作业 Calculator plusplus 代码传送门 PS:这次作业仍然orz感谢一位同学与一位学长的windows帮助&#xff0c;同时再次吐槽作业对Mac系统用户的不友好。&#xff08;没朋友千万别用Mac&#xff01;&#xff01;&#xff01;&#xff09; 还有想吐槽作业对规范的要求大大超…

联合体union和大小端(big-endian、little-endian)

1.联合体union的基本特性——和struct的同与不同union&#xff0c;中文名“联合体、共用体”&#xff0c;在某种程度上类似结构体struct的一种数据结构&#xff0c;共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量。在成员完全相同的情况下&#xff0c;struct比…

前端面试的作品示例_如何回答任何技术面试问题-包括示例

前端面试的作品示例Technical interviews can be extremely daunting. From the beginning of each question to the end, its important to know what to expect, and to be aware of the areas you might be asked about. 技术面试可能会非常艰巨。 从每个问题的开始到结束&a…

$(shell expr $(MAKE_VERSION) \= 3.81) 这里“\”的解释

android/build/core/main.mk $(shell expr $(MAKE_VERSION) \> 3.81) 为什么要加多一个“\”,因为">"会被shell解析为重定向符号&#xff0c;所以需要转义或用引号包围 所以&#xff0c;也可以这样写$(shell expr $(MAKE_VERSION) “>” 3.81)转载于:https:…

iOS应用模块化的思考及落地方案(一)模块的划分及模块化工作流程

1.0 什么是模块化 很多关于重构及设计模式的介绍中&#xff0c;经常提到的几个词语是复用及解耦。 模块化之所以被提出&#xff0c;也更多是为了解决这几个问题。 复用可以减少重复造轮子的情况&#xff0c;很容易理解的是&#xff0c;我们经常使用的github上的第三方框架&a…

Swiper 用法

部分常用API ininialSlide: 2, //起始图片切换的索引位置&#xff08;起始从0开始&#xff0c;默认为0&#xff09; autoplay: 3000, //设置自动切换时间&#xff0c;单位毫秒 speed: 1000, //设置滑动速度 continuous: true, //无限循环的图片切换效果 disableScroll: true, /…