JavaScript 进阶知识

您所在的位置:网站首页 js代码报错会影响后面代码执行吗 JavaScript 进阶知识

JavaScript 进阶知识

2023-04-23 18:00| 来源: 网络整理| 查看: 265

JS高级 前言 经过前面几篇文章的学习,相信大家已经对js有了大部分的理解了,但是要想真正的掌握好js,本篇才是关键。由于js高级阶段的知识点比较难理解,所以本篇文章花了大量的时间去理思路,有可能有一些知识点遗漏了,也有可能有部分知识点写的不对,欢迎大家留言纠正。 1.异常处理

常见的异常分类

运行环境的多样性导致的异常(浏览器) 语法错误,代码错误

异常最大的特征,就是一旦代码出现异常,后面的代码就不会执行。

1.1异常捕获 捕获异常,使用try-catch语句: try{ // 这里写可能出现异常的代码 }catch(e){ // e-捕获的异常对象 // 可以在此处书写出现异常后的处理代码 } 异常捕获语句执行的过程为: 代码正常运行, 如果在try中出现了错误,try里面出现错误的语句后面的代码都不再执行, 直接跳转到catch中 catch中处理错误信息 然后继续执行后面的代码 如果try中没有出现错误, 那么不走catch直接执行后面的代码

通过try-catch语句进行异常捕获之后,代码将会继续执行,而不会中断。

示例代码:

console.log('代码开始执行'); try{ console.log(num); // num 在外部是没有定义的 }catch(e){ console.log(e); console.log('我已经把错误处理了'); } console.log('代码结束执行');

效果图:

从效果图中我们可以看到,num是一个没有定义的变量,如果没有放在try-catch代码块中,后面的‘代码结束执行’就不会被打印。通过把try-catch放在代码块中,出现错误后,就不会影响后面代码的运行了,他会把错误信息打印出来。

注意:

语法错误异常用try-catch语句无法捕获,因为在预解析阶段,语法错误会直接检测出来,而不会等到运行的时候才报错。 try-catch在一般日常开发中基本用不到,但是如果要写框架什么的,用的会非常多。因为这个会让框架变得健壮

异常捕获语句的完整模式

异常捕获语句的完整模式为try-catch-finally try { //可能出现错误的代码 } catch ( e ) { //如果出现错误就执行 } finally { //结束 try 这个代码块之前执行, 即最后执行 }

finally中的代码,不管有没有发生异常,都会执行。一般用在后端语言中,用来释放资源,JavaScript中很少会用到

1.2抛出异常

如何手动的抛出异常呢?

案例:自己写的一个函数,需要一个参数,如果用户不传参数,此时想直接给用户抛出异常,就需要了解如何抛出异常。

抛出异常使用throw关键字,语法如下:

throw 异常对象;

异常对象一般是用new Error("异常消息"), 也可以使用任意对象

示例代码:

function test(para){ if(para == undefined){ throw new Error("请传递参数"); //这里也可以使用自定义的对象 throw {"id":1, msg:"参数未传递"}; } } try{ test(); }catch(e){ console.log(e); }

效果图:

1.3异常的传递机制 function f1 () { f2(); } function f2 () { f3(); } function f3() { throw new Error( 'error' ); } f1(); // f1 称为调用者, 或主调函数, f2 称为被调用者, 或被调函数

当在被调函数内发生异常的时候,异常会一级一级往上抛出。

2.面向对象编程 在了解面向对象编程之前,我们先来了解下什么是面向过程,什么是面向对象,他们之间的区别是什么。 2.1 面向过程和面向对象的的对比

举个例子:

日常洗衣服

1.面向过程的思维方式:

面向过程编程:将解决问题的关注点放在解决问题的具体细节上,关注如何一步一步实现代码细节; step 1:收拾脏衣服 step 2:打开洗衣机盖 step 3:将脏衣服放进去 step 4:设定洗衣程序 step 5:开始洗衣服 step 6:打开洗衣机盖子 step 7:晒衣服

2.面向对象的思维方式:

面向对象编程:将解决问题的关注点放在解决问题所需的对象上,我们重点找对象; 人(对象) 洗衣机(对象)

在面向对象的思维方式中:我们只关心要完成事情需要的对象,面向对象其实就是对面向过程的封装;

示例代码:

在页面上动态创建一个元素 //面向过程 //1-创建一个div var div=document.createElement('div'); //2-div设置内容 div.innerHTML='我是div'; //3-添加到页面中 document.body.appendChild(div); //面向对象 $('body').append('我也是div');

我们可以看出,jQ封装的其实就是对面向过程的封装。

总结: 面向对象是一种解决问题的思路,一种编程思想。

2.2 面向对象编程举例 设置页面中的div和p的边框为'1px solid red'

1、传统的处理办法

// 1> 获取div标签 var divs = document.getElementsByTagName( 'div' ); // 2> 遍历获取到的div标签 for(var i = 0; i < divs.length; i++) { //3> 获取到每一个div元素,设置div的样式 divs[i].style.border = "1px dotted black"; } // 4> 获取p标签 var ps = document.getElementsByTagName("p"); // 5> 遍历获取到的p标签 for(var j = 0; j < ps.length; j++) { // 获取到每一个p元素 设置p标签的样式 ps[j].style.border = "1px dotted black"; }

2、使用函数进行封装优化

// 通过标签名字来获取页面中的元素 function tag(tagName) { return document.getElementsByTagName(tagName); } // 封装一个设置样式的函数 function setStyle(arr) { for(var i = 0; i < arr.length; i++) { // 获取到每一个div或者p元素 arr[i].style.border = "1px solid #abc"; } } var dvs = tag("div"); var ps = tag("p"); setStyle(dvs); setStyle(ps);

3、使用面向对象的方式

// 更好的做法:是将功能相近的代码放到一起 var obj = { // 命名空间 getEle: { tag: function (tagName) { return document.getElementsByTagName(tagName); }, id: function (idName) { return document.getElementById(idName); } // ... }, setCss: { setStyle: function (arr) { for(var i = 0; i < arr.length; i++) { arr[i].style.border = "1px solid #abc"; } }, css: function() {}, addClass: function() {}, removeClass: function() {} // ... } // 属性操作模块 // 动画模块 // 事件模块 // ... }; var divs = obj.getEle.tag('div'); obj.setCss.setStyle(divs); 2.3 面向对象的三大特性 面向对象的三大特性分别是:'封装','继承','多态'。

1、封装性

对象就是对属性和方法的封装,要实现一个功能,对外暴露一些接口,调用者只需通过接口调用即可,不需要关注接口内部实现原理。

js对象就是“键值对”的集合

键值如果是数据( 基本数据, 复合数据, 空数据 ), 就称为属性 如果键值是函数, 那么就称为方法 对象就是将属性与方法封装起来 方法是将过程封装起来

2、继承性

所谓继承就是自己没有, 别人有,拿过来为自己所用, 并成为自己的东西

2.1、传统继承基于模板

子类可以使用从父类继承的属性和方法。

class Person { string name; int age; } class Student : Person { } var stu = new Student(); stu.name

即:让某个类型的对象获得另一个类型的对象的属性的方法

2.2、js 继承基于对象

在JavaScript中,继承就是当前对象可以使用其他对象的方法和属性。

js继承实现举例:混入(mix)

// 参数o1和o2是两个对象,其中o1对象继承了所有o2对象的“k”属性或者方法 var o1 = {}; var o2 = { name: 'Levi', age: 18, gender: 'male' }; function mix ( o1, o2 ) { for ( var k in o2 ) { o1[ k ] = o2[ k ]; } } mix(o1, o2); console.log(o1.name); // "Levi"

3、多态性(基于强类型,js中没有多态)只做了解

同一个类型的变量可以表现出不同形态,用父类的变量指向子类的对象。 动物 animal = new 子类(); // 子类:麻雀、狗、猫、猪、狐狸... 动物 animal = new 狗(); animal.叫(); 2.4 创建对象的方式

1、字面量 {}

var student1 = { name:'诸葛亮', score:100, code:1, } var student2 = { name:'蔡文姬', score:98, code:2, } var student3 = { name:'张飞', score:68, code:3, }

字面量创建方式,代码复用性太低,每一次都需要重新创建一个对象。

2、Object()构造函数

var student1 = new Object(); student1.name = '诸葛亮'; student1.score = 100; student1.code = 1; var student2 = new Object(); student2.name = '蔡文姬'; student2.score = 98; student2.code = 2; var student3 = new Object(); student3.name = '张飞'; student3.score = 68; student3.code = 3;

代码复用性太低,字面量创建的方式其实就是代替Object()构造函数创建方式的。

3、自定义构造函数

自定义构造函数,可以快速创建多个对象,并且代码复用性高。 // 一般为了区分构造函数与普通函数,构造函数名首字母大写 function Student(name,score,code){ this.name = name; this.score = score; this.code = code; } var stu1 = new Student('诸葛亮',100,1); var stu2 = new Student('蔡文姬',98,2); var stu3 = new Student('张飞',68,3);

构造函数语法:

构造函数名首字母大写; 构造函数一般与关键字:new一起使用; 构造函数一般不需要设置return语句,默认返回的是新创建的对象; this指向的是新创建的对象。

构造函数的执行过程:

new关键字,创建一个新的对象,会在内存中开辟一个新的储存空间; 让构造函数中的this指向新创建的对象; 执行构造函数,给新创建的对象进行初始化(赋值); 构造函数执行(初始化)完成,会将新创建的对象返回。

构造函数的注意点:

构造函数本身也是函数; 构造函数有返回值,默认返回的是新创建的对象; 但是如果手动添加返回值,添加的是值类型数据的时候,构造函数没有影响。如果添加的是引用类型(数组、对象等)值的时候,会替换掉新创建的对象。 function Dog(){ this.name="哈士奇"; this.age=0.5; this.watch=function(){ console.log('汪汪汪,禁止入内'); } // return false; 返回值不会改变,还是新创建的对象 // return 123; 返回值不会改变,还是新创建的对象 // return [1,2,3,4,5]; 返回值发生改变,返回的是这个数组 return {aaa:'bbbb'}; // 返回值发生改变,返回的是这个对象 } var d1=new Dog(); // 新创建一个对象 console.log(d1); 构造函数可以当做普通函数执行,里面的this指向的是全局对象window。 function Dog(){ this.name="husky"; this.age=0.5; this.watch=function(){ console.log('汪汪汪,禁止入内'); } console.log(this); // window对象 return 1; } console.log(Dog()); // 打印 1 2.5 面向对象案例 通过一个案例,我们来了解下面向对象编程(案例中有一个prototype概念,可以学完原型那一章后再来看这个案例)。

需求:

实现一个MP3音乐管理案例; 同种类型的MP3,厂家会生产出成百上千个,但是每个MP3都有各自的样式、使用者、歌曲; 每个MP3都有一样的播放、暂停、增删歌曲的功能(方法);

图解:

示例代码:

// 每个MP3都有自己的 主人:owner 样式:color 歌曲:list function MP3(name,color,list){ this.owner = name || 'Levi'; // 不传值时默认使用者是‘Levi’ this.color = color || 'pink'; this.musicList = list || [ {songName:'男人哭吧不是罪',singer:'刘德华'}, {songName:'吻别',singer:'张学友'}, {songName:'对你爱不完',singer:'郭富城'}, {songName:'今夜你会不会来',singer:'黎明'} ]; } // 所有的MP3都有 播放 暂停 音乐 增删改查的功能 MP3.prototype = { // 新增 add:function(songName,singer){ this.musicList.push({songName:songName,singer:singer}); }, // 查找 select:function(songName){ for(var i=0;i 4 8.2 Function 构造函数创建函数 上面我们知道了如何通过Function构造函数创建一个空的函数,这里我们对它的传参详细的说明下。

1、不传参数时

// 不传参数时,创建的是一个空的函数 var fn1 = new Function(); fn1(); // 调用函数

2、只传一个参数

// 只传一个参数的时候,这个参数就是函数体 // 语法:var fn = new Function(函数体); var fn2 = new Function('console.log(2+5)'); f2(); // 7

3、传多个参数

// 传多个参数的时候,最后一个参数为函数体,前面的参数都是函数的形参名 // 语法:var fn = new Function(arg1,arg2,arg3.....argn,metthodBody); var fn3 = new Function('num1','num2','console.log(num1+num2)'); f3(5,2); // 7 8.3 Function 的使用

1、用Function创建函数的方式封装一个计算m - n之间所有数字的和的函数

//求 m-n之间所有数字的和 //var sum=0; //for (var i = m; i + - * / = document.querySelector('button').onclick=function(){ var num1 = document.querySelector('.num1').value; var num2 = document.querySelector('.num2').value; var operator = document.querySelector('.operator').value; // result其实最终获得的就是 num1 + operator + num2的字符串 但是他能够直接执行并计算 var result = eval(num1 + operator + num2); //计算 document.querySelector('.result').value = result; //显示 }

效果图:

8.4 Function 的原型链结构 在7.2章节中我们知道函数也还可以通过构造函数的方式创建出来,既然可以通过构造函数的方式创建,那么函数本身也是有原型对象的。

示例代码:

// 通过Function构造函数创建一个函数test var test = new Function(); // 既然是通过构造函数创建的,那么这个函数就有指向的原型 console.log(test.__proto__); // 打印出来的原型是一个空的函数 console.log(test.__proto__.__proto__); // 空的函数再往上找原型是一个空的对象 console.log(test.__proto__.__proto__.__proto__); // 再往上找就是null了 // 函数原型链: test() ---> Function.prototype ---> Object.prototype ---> null

如图所示:

通过上图,可以直观的看出,函数也是有原型的。那一个完整的原型链究竟是什么样子的呢?下面我们一起做个总结。

8.5 完整的原型链

绘制完整原型链的步骤:

1、先将一个对象的原型画出来 2、再把对象的原型的原型链画出来 ,到null结束 3、把对象的构造函数的原型链画出来 4、把Function和Object的原型关系给画出来

示例代码:

// 创建一个构造函数 function Person(){ this.name = 'Levi'; this.age = 18; } // 实例化一个对象 var p = new Person();

如图所示:

总结:

Function构造函数的原型,在Object的原型链上; Object构造函数的原型,在Function的原型链上; 9.arguments对象 在每一个函数调用的过程中, 函数代码体内有一个默认的对象arguments, 它存储着实际传入的所有参数。

示例代码:

// 封装一个加法函数 function add(num1,num2){ console.log(num1+num2); } add(1); // NaN add(1,2); // 3 add(1,2,3); // 3

在调用函数时,实参和形参的个数可以不一样,但是没有意义。

在函数内部有个arguments对象(注意:是在函数内部),arguments是一个伪数组对象。它表示在函数调用的过程中传入的所有参数(实参)的集合。在函数调用过程中不规定参数的个数与类型,可以使得函数调用变得非常灵活性。 function add(num1,num2){ console.log(arguments); // 打印的是一个伪数组 } add(1,2,3,4);

length:表示的是实参的个数; callee:指向的就是arguments对象所在的函数;

示例代码:

封装一个求最大值的函数,因为不知道需要传进多少实参,所以直接用伪数组arguments获取调用的实参 function max(){ // 假使实参的第一个数字最大 var maxNum = arguments[0]; // 循环这个伪数组 for(var i = 0; i < arguments.length; i++){ if(maxNUm < arguments[i]){ maxNUm = arguments[i]; } return maxNum; } } // 调用 console.log(max(1,9,12,8,22,5)); // 22 10. 函数的四种调用模式 四种调用模式分别是:“函数调用模式”、“方法调用模式”、“构造器调用模式”、“上下文调用模式”。

其实就是分析this是谁的问题。只看函数是怎么被调用的,而不管函数是怎么来的。

分析this属于哪个函数; 分析这个函数是以什么方式调用的;

什么是函数? 什么是方法?

如果一个函数是挂载到一个对象中,那么就把这个函数称为方法

如果一个函数直接放在全局中,由Window对象调用,那么他就是一个函数。

// 函数 function fn() {} var f = function() {}; fn(); f(); // 方法 var obj = { say: function() {} }; obj.say();

fn和f都是函数,say是一个方法

10.1 函数模式 函数模式其实就是函数调用模式,this是指向全局对象window的。 this -> window

示例代码:

// 函数调用模式: // 创建的全局变量相当于window的属性 var num = 999; var fn = function () { console.log(this); // this 指向的是 window 对象 console.log(this.num); // 999 }; fn(); 10.2 方法模式 方法模式其实就是方法调用模式,this是指向调用方法的对象。 this -> 调用方法的对象

示例代码:

// this指向的是obj var age = 38; var obj = { age: 18, getAge: function () { console.log(this); // this指向的是对象obj {age:18,getAge:f()} console.log(this.age); // 18 } }; obj.getAge(); // getAge() 是对象 obj 的一个方法 10.3 构造器模式 构造器模式其实就是构造函数调用模式,this指向新创建出来的实例对象。 this -> 新创建出来的实例对象

示例代码:

// this指向的是实例化出来的对象 function Person(name){ this.name = name; console.log(this); } var p1 = new Person('Levi'); // Person {name: "Levi"} var p2 = new Person('Ryan'); // Person {name: "Ryan"}

构造函数的返回值:

如果返回的是基本类型 function Person() { return 1; } var p1 = new Person(); console.log(p1); // 打印Person {}

构造函数内有返回值,且是基本类型的时候,返回值会被忽略掉,返回的是实例出来的对象。

如果返回的是引用类型 function Person() { return { name: 'levi', age: 18 }; } var p1 = new Person(); console.log(p1); // 此时打印 Object {name: 'levi', age: 18}

构造函数内的返回值是一个引用类型的时候,返回的就是这个指定的引用类型。

10.4 上下文(借用方法)模式 上下文,即环境,用于指定方法内部的this,上下文调用模式中,this可以被随意指定为任意对象。

上下文模式有两种方法,是由函数调用的:

函数名.apply( ... ); 函数名.call( ... );

1、apply 方法

语法:

fn.apply(thisArg, array);

参数:

第一个参数:表示函数内部this的指向(或者:让哪个对象来借用这个方法) 第二个参数:是一个数组(或者伪数组),数组中的每一项都将作为被调用方法的参数

示例代码:

// 没有参数 function fn (){ console.log(this.name); } var obj = { name : 'Levi丶' } // this 指向 obj,fn 借用obj方法里面的 name 属性 fn.apply(obj); // 打印 'Levi丶' // 有参数 function fn (num1, num2){ console.log(num1 + num2); } var obj = {} // this 指向 obj,数组中的数据是方法 fn 的参数 fn.apply(obj, [1 , 2]); // 打印 3

注意:apply方法的第一个参数,必须是一个对象!如果传入的参数不是一个对象,那么这个方法内部会将其转化为一个包装对象。

function fn() { console.log(this); } fn.apply(1); // 包装对象 fn.apply('abc'); // 包装对象 fn.apply(true); // 包装对象

指向window的几种方式:

function fn(){ } fn.apply(window); fn.apply(); fn.apply(null); fn.apply(undefined);

具体应用:

求数组中的最大数 // 以前的方法,假设第一项最大,然后与后面每一项比较,得到最大的项 var arr = [1, 3, 6, 10, 210, 23, 33, 777, 456]; var maxNum = arr[0]; for(var i = 1; i < arr.length; i++) { if(maxNum < arr[i]) { maxNum = arr[i]; } } console.log(maxNum); // 777 // 利用 内置对象的 apply 的方法 var arr = [1, 3, 6, 10, 210, 23, 33, 777, 456]; // max 是内置对象 Math 求最大值的一个方法 var maxNum = Math.max.apply(null, arr); console.log(maxNum); // 777 将传进的参数每一项之间用“-”连接 // 思考:参数个数是用户随机传的,没有具体的一个值,这时候就需要用到 arguments 的概念了 function fn (){ // 数组原型中有一个join方法,他的接收的参数是一个字符串 // join.apply的第一个参数指向 arguments 对象,第二个参数是jion方法需要的参数 return Array.prototype.join.apply(arguments, ['-']); } var ret = fn('a', 'b', 'c', 'd', 'e'); console.log(ret); // 'a-b-c-d-e'

2、call 方法

call方法的作用于apply方法的作用相同,唯一不同的地方就是第二个参数不同。

语法:

fn.apply(thisArg, parm1,parm2,parm3,...);

参数:

第一个参数:表示函数内部this的指向(或者:让哪个对象来借用这个方法) 第二个及后面的参数:不是之前数组的形式了,对应方法调用的每一个参数

示例代码:

function fn(num1, num2, num3) { console.log(num1, num2, num3); } var obj = {}; fn.call(obj, [1, 3, 9], 0, 1); // [1, 3, 9] 0 1 fn.call(obj, [1, 3, 9]); // [1, 3, 9] undefined undefined

3、apply 和 call 的区别

两者在功能上一模一样,唯一的区别就是第二个参数传递的类型不一样。

什么时候用apply?什么时候用call呢?

其实用哪个都可以,在参数少的情况下,我们可以使用call方法,但是如果参数是伪数组或者是数组的时候,call方法就不适用了,还需要将伪数组中的每一项取出来作为方法的参数,此时apply更加实用。

10.5 面试题分析

面试题1:

var age = 38; var obj = { age: 18, getAge: function() { function foo() { console.log(this.age); // 这里的this属于函数 foo; 打印 38 } foo(); // foo函是Window对象调用的 } }; obj.getAge();

面试题2:

// 只看函数是怎么被调用的,而不管函数是怎么来的 var age = 38; var obj = { age: 18, getAge: function() { alert(this.age); } }; var f = obj.getAge; f(); // 函数是Window对象调用的,所以this指向Window对象。打印:38

面试题3:

var length = 10; function fn(){ console.log(this.length); } var obj = { length: 5, method: function (fn) { fn(); // window对象调用 打印 10 arguments[0](); // 方法调用模式,是arguments对象调用的 // this指向arguments,所以arguments.length = 2; (arguments.length:实参的个数)所以打印 2 } }; obj.method(fn, 123);

面试题4:

怎么使用call或者apply方法实现构造函数的复用呢? function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; } function Teacher(name, age, gender, workYear, subject) { this.name = name; this.age = age; this.gender = gender; this.workYear = workYear; this.subject = subject; } function Student(name, age, gender, stuNo, score) { this.name = name; this.age = age; this.gender = gender; this.stuNo = stuNo; this.score = score; } var tec = new Teacher('张老师', 32, 'male', '7年', '语文'); var stu = new Student('xiaowang', 18, 'male', 10001, 99); console.log(tec); // Teacher {name: "张老师", age: 32, gender: "male", workYear: "7年", subject: "语文"} console.log(stu); // Student {name: "xiaowang", age: 18, gender: "male", stuNo: 10001, score: 99}

上面的代码中一个Teacher构造函数,一个Student构造函数,他们都有一些公共的属性,跟Person构造函数里面的属性重复,我们能否使用call或者apply方法,简化上面的代码呢?

function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; } function Teacher(name, age, gender, workYear, subject) { // 借用 Person 函数来给当前对象添加属性 Person.call(this, name, age, gender); // 这里的this指向的就是当前的Teacher构造函数 this.workYear = workYear; this.subject = subject; } function Student(name, age, gender, stuNo, score) { Person.call(this, name, age, gender); // 这里的this指向的就是当前的Student构造函数 this.stuNo = stuNo; this.score = score; } var tec = new Teacher('张老师', 32, 'male', '7年', '语文'); var stu = new Student('xiaowang', 18, 'male', 10001, 99); console.log(tec); // Teacher {name: "张老师", age: 32, gender: "male", workYear: "7年", subject: "语文"} console.log(stu); // Student {name: "xiaowang", age: 18, gender: "male", stuNo: 10001, score: 99} 11.递归 11.1 什么是递归 什么是递归?递归就是函数直接自己调用自己或者间接的调用自己。

举个例子:

函数直接调用自己 function fn(){ fn(); } fn(); 函数间接调用自己 function fn1(){ fn2(); } function fn2(){ fn1(); }

递归示例代码:

function fn (){ console.log('从前有座山,'); console.log('山里有座庙,'); console.log('庙里有个老和尚,'); console.log('老和尚给小和尚讲,'); fn(); } fn(); // 产生递归,无限打印上面的内容

这样做会进入到无限的死循环当中。

11.2 化归思想 化归思想是将一个问题由难化易,由繁化简,由复杂化简单的过程称为化归,它是转化和归结的简称。

合理使用递归的注意点:

函数调用了自身 必须有结束递归的条件,这样程序就不会一直运行下去了

示例代码: 求前n项的和

求前n项的和其实就是:1 + 2 + 3 +...+ n; 寻找递推关系,就是n与n-1, 或n-2之间的关系:sum(n) == n + sum(n - 1); 加上结束的递归条件,不然会一直运行下去。 function sum(n){ if(n == 1) return 1; // 递归结束条件 return n + sum(n - 1); } sum(100); // 打印 5050

递推关系:

11.3 递归练习

1、求n的阶乘:

思路:

f(n) = n * f(n - 1); f(n - 1) = (n - 1) * f(n - 2);

示例代码:

function product(n){ if(n == 1) { return 1; } return n * product(n-1); } console.log(product(5)); // 打印 120

2、求m的n次幂:

思路:

f(m,n) = m * f(m,n-1);

示例代码:

function pow(m,n){ if(n==1){ return m; } return m * pow(m,n-1); } console.log(pow(2, 10)); // 打印 1024

3、斐波那契数列

思路:什么是斐波那契数列?

1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55,...

数字从第三项开始,每一项都等于前两项的和。可得出公式:fn = f(n-1) + f(n-2),结束递归的条件:当n


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3