递归
递归: 函数里面调用自己的函数
注意: 一定要有函数结束的条件
将大的操作划分小操作重复执行的时候使用
报错: 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)); // Arrayapply
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);
原型链
原型链: 在对象或函数被创建的时候所自带的链表关系, 实现继承和查找;