递归
递归: 函数里面调用自己的函数
注意: 一定要有函数结束的条件
将大的操作划分小操作重复执行的时候使用
报错: Uncaught RangeError: Maximum call stack size exceeded 栈溢出 ---> 原因: 递归函数没有设置结束条件
// 阶乘: 6! = 6 * 5 * 4 * 3 * 2 * 1 // function jc(n) { // return n * jc(n - 1); // } function jc(n) { // 设置结束条件 if(n == 1){ return 1; } return n * jc(n - 1); } var s = jc(6); console.log(s); console.log(jc(10)); console.log(jc(100));
斐波那契数列
有名的兔子问题: 出生2个月后每个月都会生产一对新兔子
月份 兔子
1 1 1
2 1 1
3 2 1 1
4 3 1 1 1
5 5 1 1 1 1 1
6 8 1 1 1 1 1 1 1 1
7 13 1 1 1 1 1 1 1 1 1 1 1 1 1
序列: 1 1 2 3 5 8 13 21
当前月的兔子 = 上个月的兔子个数 + 上上个月的兔子个数
第二个月和第一个月的时候 个数都是1
用函数求第n个月的兔子个数
function fib(n) { // 设置返回值 结束条件 if(n == 1 || n == 2){ return 1; } return fib(n-1) + fib(n-2); }; console.log(fib(6)); console.log(fib(12)); /* 第6个月的兔子 = 5 + 4 = 4 + 3 + 3 + 2 = 3 + 2 + 2 + 1 + 2 + 1 + 2 = 2 + 1 + 2 + 2 + 1 + 2 + 1 + 2 */
快速排序
快速排序: 找到中间项 进行左右重复排序的过程
\1. 有一个函数处理排序函数
\2. 找到中间项的下标
\3. 找到中间项
\4. 删除中间项
\5. 创建两个空数组 放置比中间项小 和 大的值
\6. 用数组的每一个项和中间项做比较 比他大的放右边 比她小的放左边
\7. 左右数组需要重复排序
\8. 设置每次排序以后的返回结果
\9. 设置排序结束条件: 当穿进去的数组长度小于等于1 的时候 直接返回
var arr = [32, 12, 435, 56, 21, 78, 90]; // function qs(array) { // // 9. 设置排序结束条件: 当穿进去的数组长度小于等于1 的时候 直接返回 // if(array.length <= 1){ // return array; // } // // 2. 找到中间项的下标 // var num = Math.floor(array.length / 2); // // console.log(num); // // 3. 找到中间项 // var val = array[num]; // // 4. 删除中间项 // array.splice(num, 1); // // 5. 创建两个空数组 放置比中间项小 和 大的值 // var left = [], right = []; // // 6. 用数组的每一个项和中间项做比较 比他大的放右边 比她小的放左边 // for(var i = 0; i < array.length; i++){ // if(array[i] > val){ // right.push(array[i]); // } else { // left.push(array[i]); // } // } // // 7. 左右数组需要重复排序 // left = qs(left); // right = qs(right); // // 8. 设置每次排序以后的返回结果 // return left.concat(val, right); // }; function qs(array) { // 9. 设置排序结束条件: 当穿进去的数组长度小于等于1 的时候 直接返回 if(array.length <= 1){ return array; } // 2. 找到中间项的下标 var num = Math.floor(array.length / 2); // 3-4: var val = array.splice(num, 1)[0]; // 5. 创建两个空数组 放置比中间项小 和 大的值 var left = [], right = []; // 6. 用数组的每一个项和中间项做比较 比他大的放右边 比她小的放左边 for(var i = 0; i < array.length; i++){ if(array[i] > val){ right.push(array[i]); } else { left.push(array[i]); } } // 7-8 return qs(left).concat(val, qs(right)); }; var a =qs(arr); console.log(a);
事件频繁触发
为了解决事件频繁触发的问题 引申出了防抖和节流
防抖
防抖: 利用闭包, 在用户触发的事件过程中不进行事件的处理 等待用户停止触发多长时间后 再进行事件的处理
只要用户在输入或者是移动 就不触发事件处理函数 等用户停止1s后 在执行事件处理函数
只要用户触发 时间需要重新计时
问题: 太生硬
// 防抖函数 function debounce(fn, wait) { // 解决全局变量的问题 var timer = null; return function () { // 清除定时器 clearTimeout(timer); // 延迟定时器 timer = setTimeout(fn, wait); }; }
// 获取元素 var div = document.querySelector('div'); var n = 0; // 添加事件 div.onmousemove = debounce(inner, 3000); function inner() { n++; div.innerHTML = n; };
节流
节流: 在一定的时间内 只能触发一次事件处理函数
利用闭包 为了解决全局变量
// 节流函数 function throttle(fn, wait) { // 假设当前可以触发函数 var tag = true; var timer = null; return function () { // 判断当前是否可以触发函数 if(tag){ // 将状态变成不可触发 tag = false; // 开启定时器 timer = setTimeout(function () { fn(); // 将状态变成可触发 tag = true; clearTimeout(timer); }, wait); } }; }
// 获取元素 var div = document.querySelector('div'); var n = 0; // 添加事件 div.onmousemove = throttle(inner, 100); function inner() { n++; div.innerHTML = n; };
call和apply
call和apply: 改变this指向
区别:
函数.call(this的新指向, ...data); 参数一个个用,分隔直接写
函数.apply(this的新指向, [...data]); 第二个参数是一个数组
call
var obj = { name: '张三', sayName: function () { console.log(this.name); } }; obj.sayName(); // 张三 var obj1 = { name: '李四' }; obj.sayName.call(obj1); // 李四 function fn() { console.log(this); } fn(); // window fn.call(document); fn.call(undefined); fn.call(1); fn.call('a'); function fn1(a, b) { console.log(this, a, b); } fn1(20, 30); // window 20 30 fn1.call(1, 10, 20); // 1 10 20 // 判断数据的类型 console.log(Object.prototype.toString()); // [object Object] console.log(Object.prototype.toString.call(1)); // [object Number] console.log(Object.prototype.toString.call('aaa')); // [object String] console.log(Object.prototype.toString.call({})); // [object Object] console.log(Object.prototype.toString.call([])); // [object Array] console.log(Object.prototype.toString.call({}).slice(8, -1)); // Object console.log(Object.prototype.toString.call([]).slice(8, -1)); // Array
apply
function fn1(a, b) { console.log(this, a, b); } fn1.apply(document, [44, 33]); // 数组找最大和最小项 var arr = [32, 43, 546, 56, 32, 534, 234, 65]; console.log(Math.max.apply(this, arr)); console.log(Math.min.apply(this, arr));
面向对象
概念
概念: 基于对象 面向过程: 一步步分析实现 注重过程 面向对象: 一种编程思想 注重结果 核心: 对象 组成: 方法: 函数 行为 属性: 描述 属性 以类创建模板 实例化对象的过程 es5中 没有明确的类的概念 用函数去创建模板 实例化对象 实例化: 具象化对象的时候 就是实例化 遵循: 有对象就要用对象 没有对象就创建对象 特点: 封装 继承 多态
创建对象
字面量
字面量: 只适用于单个对象的创建
var obj = { name: '张三', age: 33, work: function () { console.log('为了碎银几两 辛辛苦苦工作'); } }; console.log(obj); console.log(obj.name); obj.work();
new关键字
new关键字创建: 代码冗余
// 1. 创建空对象 var obj = new Object(); // 2. 添加属性和方法 obj.name = '杨洋'; obj.age = 38; obj.work = function () { console.log('演员'); }; console.log(obj); console.log(obj.name); obj.work();
工厂模式创建
问题:
\1. 识别不清
\2. 内存浪费
// 工厂 function createObj(name, age) { // 1. 创建空对象 var obj = new Object(); // 2. 添加属性和方法 obj.name = name; obj.age = age; obj.work = function () { console.log('工作去吧'); }; // 3. 出厂 设置返回值 return obj; }; // 实例化对象 var obj = createObj('张三', 33); var obj1 = createObj('古力娜扎', 28); console.log(obj, obj1); console.log(obj.name, obj1.name); obj.work(); obj1.work(); // 判断对象是否是由createObj函数创建 // 对象 instanceof 函数 true--是 false--不是 console.log(obj instanceof createObj); // false console.log(obj.work == obj1.work); // false
构造函数
构造函数: 1. 构造函数首字母大写 为了和普通函数进行区分 (约定) 2. 方法和属性直接加给this 3. 必须使用new进行调用, 否则和普通函数没有区别 new的发生了什么: 1. 创建一个空对象 2. 将this指向当前空对象 3. 将函数prototype 赋值给 对象的__proto__ 4. 添加属性和方法 5. 隐式返回对象 问题: 1. 内存浪费
function CreateObj(name, age){ // // 1. 创建空对象 // var obj = new Object(); // 2. 添加属性和方法 this.name = name; this.age = age; this.work = function () { console.log('工作去吧'); }; // // 3. 出厂 设置返回值 // return obj; } // 实例化: var obj = new CreateObj('迪丽热巴', 38); var obj1 = new CreateObj('古力娜扎', 38); console.log(obj, obj1); console.log(obj instanceof CreateObj); // true console.log(obj.work == obj1.work); // false
原型创建
问题: 不能传参
// 1. 构造函数 function CreateObj() { }; // 2. 给原型添加属性和方法 CreateObj.prototype.name = '迪丽热巴'; CreateObj.prototype.age = 39; CreateObj.prototype.work = function () { console.log('工作'); }; // 3. 实例化对象 var obj = new CreateObj(); var obj1 = new CreateObj(); console.log(obj, obj1); console.log(obj.name, obj1.name); // 原型链: 在对象或函数被创建的时候所自带的链表关系, 实现继承和查找; // 找: 先找自身, 在找原型属性 在找object 找到直接返回 找到object都没有 返回undefined obj.work(); obj1.work(); console.log(obj.work == obj1.work); // true
混合创建
构造创建(可变的) + 原型创建(不变的)
// 1. 构造函数 function CreateObj(name, age) { this.name = name; this.age = age; }; // 2. 给原型添加属性和方法 CreateObj.prototype.work = function () { console.log('工作'); }; // 3. 实例化对象 var obj = new CreateObj('张三', 23); var obj1 = new CreateObj('李四', 33); console.log(obj, obj1); obj.work(); obj1.work(); console.log(obj.work == obj1.work); // true
动态混合创建(了解)
在构造函数中判断原型上的方法和属性是否是期望值 不是的话在进行赋值
// 1. 构造函数 function CreateObj(name, age) { this.name = name; this.age = age; if (typeof this.work == 'function') { console.log('已经是函数'); } else { console.log('不是函数'); // 2. 给原型添加属性和方法 CreateObj.prototype.work = function () { console.log('工作'); }; } }; // 3. 实例化对象 var obj = new CreateObj('张三', 23); var obj1 = new CreateObj('李四', 33); console.log(obj, obj1); obj.work(); obj1.work(); console.log(obj.work == obj1.work); // true
原型
原型: 用来存储最顶层共享的方法和属性的对象 函数: prototype 对象: __proto__
var arr = [1,2,3,4]; arr.push(5); console.log(arr); console.log(arr.__proto__); var brr = new Array(2,3,4,5); console.log(brr); console.log(brr.__proto__); console.log(Array.prototype); console.log(Array.prototype == brr.__proto__); // true Array.prototype.push = function () { console.log('新方法'); }; brr.push(666); console.log(brr);
原型链
原型链: 在对象或函数被创建的时候所自带的链表关系, 实现继承和查找;