var per = {
name: "张三",
age: 18,
sayHi: function() {}
};
var car = {
brand: "奔驰",
color: red,
driving: function() {}
};
// 将函数封装成对象 避免与全局变量污染的问题 解决代码冗余
var obj = {
getTag: function(str) {
return document.getElementByTagName(str);
},
setStyle: function(list) {
for (var i = 0; i < list.length; i++) {
list[i].style.backgroundColor = "red";
}
}
};
var pList = obj.getTag("p");
//通过对象调用方法
obj.setStyle(pList);
$("p").css("backgroundColor", "red");
通过 new 关键字的函数 都是构造函数
所有的对象都是构造函数创建的
内置对象也是构造函数创建的
[]
底层是调用了 new Array()
{}
底层是调用了 new Object()
function
底层是调用了 new Function()
Date
底层是调用了 new Date()
自定义构造函数
function Person(name, age) {
this.name = name;
this.age = age;
// 不同对象调用构造函数中的方法 不是同一个函数
this.sayHi = function() {
console.log("我的名字" + this.name + "我的年龄" + this.age);
};
}
//实例化对象(通过构造函数创建对象的过程)
//由于预解析 会将变量提升 所以会先开辟p1空间 在执行new关键字的四个步骤
var p1 = new Person("张三", 18);
var p2 = new Person("李四", 20);
/*new关键字工作原理
1.创建一个空对象
2.将this关键字指向这个空对象
3.执行构造函数代码 完成对象的赋值
4.自动返回这个对象
*/
// 不同对象调用构造函数中的方法 不是同一个函数
console.log(p1.sayHi == p2.sayHi); //false
//构造函数中的属性存的是数据 而方法存的是地址 不同对象的地址不同 所以不是同一个函数
//弊端: 每调用一次构造函数 就会创建一个新的函数 浪费内存资源
function fn() {
console.log("我的名字" + this.name + "我的年龄" + this.age);
}
function Person(name, age) {
this.name = name;
this.age = age;
// 将构造函数中的方法提取出来 引用类型赋值拷贝的是地址
this.sayHi = fn;
}
//实例化对象(通过构造函数创建对象的过程)
//由于预解析 会将变量提升 所以会先开辟p1空间 在执行new关键字的四个步骤
var p1 = new Person("张三", 18);
var p2 = new Person("李四", 20);
/*new关键字工作原理
1.创建一个空对象
2.将this关键字指向这个空对象
3.执行构造函数代码 完成对象的赋值
4.自动返回这个对象
*/
// 不同对象调用构造函数中的方法 是同一个地址 是同一个函数
console.log(p1.sayHi == p2.sayHi); //true
var obj = {
sayHi: function() {
console.log("我的名字" + this.name + "我的年龄" + this.age);
}
};
//此时不能只用用函数式 要用函数表达式 因为函数式会将整个函数提升 函数里面obj是undefined 所以会报错
/* function Person(name,age){
this.name=name;
this.age=age;
this.sayHi= obj.fn;
}; */
// 函数表达式 变量只会变量名提升 其他在原地 所以不会报错
var Person = function(name, age) {
this.name = name;
this.age = age;
// 引用类型赋值拷贝的是地址 但是如果需要创建狗狗的构造函数 又要创建一个与之对应的对象来存储方法
//也就是说每次创建一个构造函数 就要声明一个与之对应的对象来存储方法
this.sayHi = obj.sayHi;
};
//实例化对象(通过构造函数创建对象的过程)
//由于预解析 会将变量提升 所以会先开辟p1空间 在执行new关键字的四个步骤
var p1 = new Person("张三", 18);
var p2 = new Person("李四", 20);
/*new关键字工作原理
1.创建一个空对象
2.将this关键字指向这个空对象
3.执行构造函数代码 完成对象的赋值
4.自动返回这个对象
*/
// 不同对象调用构造函数中的方法 是同一个地址 是同一个函数
console.log(p1.sayHi == p2.sayHi); //true
prototype
属性 存储的是原型对象的地址 所以构造函数指向与之对应的原型对象构造函数名.prototype
constructor
属性 指向构造函数var Person = function(name, age) {
this.name = name;
this.age = age;
};
//构造函数的原型中动态添加成员
Person.prototype.sayHi = function() {
console.log("我的名字" + this.name);
};
//构造函数访问原型对象中的成员
Person.prototype.sayHi();
//由这个构造函数实例化的每一个对象
var p1 = new Person("张三", 18);
p1.sayHi();
var p2 = new Person("李四", 20);
p2.sayHi();
//都指向原型中的成员 都是一个方法
console.log(p1.sayHi == p2.sayHi); //true
console.log(p1.sayHi == Person.prototype.sayHi); //true
__proto__
属性 不是W3C的标准属性 有些浏览器不支持__proto__
属于构造函数的 prototypeconsole.log(p1.__proto__ === Person.prototype);//true
构造函数与原型对象与实例化对象之间的三角关系
构造函数prototype属性指向原型对象
原型对象construct属性指向构造函数
实例化对象__proto__属性指向与之对应构造函数的原型对象
__proto__
属于构造函数的 prototype
console.log(p1.__proto__ === Person.prototype);//true
原型中constructor属性的注意点
如果修改了原型对象 constructor属性会丢失
解决方案 手动添加constructor执行构造函数名
function Stu(name, age) {
this.name = name;
this.age = age;
}
//点语法给原型对象动态添加成员
Stu.prototype.study = function () {
console.log("学习ing");
};
var s1 = new Stu('张三', 20);
s1.study();
console.log(s1);
//修改原型的指向
Stu.prototype = {
study() {
console.log('修改了');
},
//constructor属性丢失 手动添加即可
constructor: Stu
}
var s1 = new Stu('张三', 20);
console.log(s1.constructor);
所有实例对象共有的成员都可以放在原型对象中
function Stu(name, age) {
this.name = name;
this.age = age;
}
//原型中有study方法
Stu.prototype.study = function () {
console.log("原型对象中的方法");
};
var s1 = new Stu('张三', 20);
//实例化对象中的study方法
s1.study = function () {
console.log('实例化对象中的方法');
}
s1.study();
function Stu(name, age) {
this.name = name;
this.age = age;
}
//原型中有study方法
Stu.prototype.study = function () {
console.log("原型对象中的方法");
};
//实例化对象
// 在修改之前实例化 访问修改前的原型对象中的成员
// var s1 = new Stu('张三', 20);
// s1.study();
//修改原型对象的指向
Stu.prototype = {
study: function () {
console.log('修改后的原型对象');
}
}
// 在修改之后实例化 访问修改后的原型对象中的成员
var s1 = new Stu('张三', 20);
s1.study();
.原型对象
不管是哪个构造函数被创建 系统都会自动帮我们生成一个与之对应的原型对象
访问原型对象的方法
构造函数名.prototype
谁可以访问原型对象
构造函数自身
构造函数实例化出来的对象
原型对象使用的注意点
① 构造函数实例化对象们共有的数据 才可以放在原型对象中
② 构造函数实例化的对象们 访问成员的规则 就近原则(先看自己的 自己有就用自己的 自己没有就看原型有没有)
③ 原型可以修改
实例化对象访问原型看是实例化对象是在修改原型之前还是在修改原型之后
④原型对象存储所有时候对象共有的数据 避免了内存资源浪费与全局变量污染
__proto
属于实例化对象 指向构造函数与之对应的原型对象
可以让同一个构造函数所有的实例化对象拥有共同的成员
constructor
属于原型对象 指向与之对应的构造函数
通过constructor
属性可以知道某个实例化对象是由哪个构造函数创建的
//方法1 混入式
// 弊端 每继承一次 就要执行forin循环
var father = {
car: {
price: 30000,
color: 'red'
}
}
var son = {
}
//son继承father
for (var key in father) {
//son中点语法动态添加成员
son[key] = father[key]
}
console.log(son);
//方法2 替换原型
var father = {
car: {
price: 30000,
color: 'red'
}
}
function Son(gf) {
this.gf = gf;
}
//将父对象作为子对象构造函数的原型对象
//那么实例化对象都拥有父对象的所有成员
//弊端 直接赋值 会丢失之前的原型对象
Son.prototype = father;
var s1 = new Son(['小乔', '大桥']);
console.log(s1);
console.log(s1.car);
var s2 = new Son(['小新', '大新']);
console.log(s2);
console.log(s2.car);
//方法3 混合式(混入式+替换原型)
var father = {
car: {
price: 30000,
color: 'red'
}
}
function Son(gf) {
this.gf = gf;
}
//遍历父对象所有的成员 添加到子对象的原型中
for (var key in father) {
Son.prototype[key] = father[key];
}
var s1 = new Son(['小乔', '大桥']);
console.log(s1);
console.log(s1.car);
var s2 = new Son(['小新', '大新']);
console.log(s2);
console.log(s2.car);
//混合式继承的封装
var father = {
car: {
price: 30000,
color: 'red'
}
}
function Son(gf) {
this.gf = gf;
}
//method子对象的构造函数
//father父对象
function extend(method, father) {
for (var key in father) {
method.prototype[key] = father[key];
}
}
//调用
//第一个参数传入构造函数 第二个参数传入父对象
extend(Son, father);
function Animal(name) {
this.name = name;
}
var dog = new Animal('狗砸');
var cat = new Animal('喵咪');
dog.eat = function (food) {
console.log('我是' + this.name + '我喜欢吃' + food);
}
cat.eat = function (food) {
console.log('我是' + this.name + '我喜欢吃' + food);
}
//人
function Person(name) {
this.name = name;
}
var p = new Person('人');
p.work = function (animal, food) {
animal.eat(food)
};
//调用
//同一个对象在不同情况的多种状态
p.work(dog, '鸡腿')
p.work(cat, '喵粮')
__proto__
属性) 也有自己的原型对象 以此类推 形成的链式结构称之为原型链 function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person('人', 20);
console.log(p.gender);//对象取值 不存在返回undefined
// p.say();//undefined没有方法 所以报错
console.log(p);
console.log(p.toString());//[object Object]
//p中没有toString方法 原型中也没有 但是原型的原型中有toString方法 所以不会报错
console.log(p.constructor);//Person构造函数
console.log(p.__proto__ === Person.prototype);//true
console.log(p.__proto__.__proto__.constructor);//Object
console.log(p.__proto__.__proto__ === Object.prototype);//true
console.log(p.__proto__.__proto__.__proto__);//null
var date = new Date();
//日期对象不能用log 会自动转成toString字符串
//查看对象内存空间 用dir
console.dir(date)
console.log(date.constructor);//Date构造函数
console.log(date.__proto__ === Date.prototype);//true
console.log(date.__proto__.__proto__.constructor);//Object
console.log(date.__proto__.__proto__ === Object.prototype);//true
console.log(date.__proto__.__proto__.__proto__);//null
var arr = [10, 20, 30];
//底层用了new Array()构造函数
// var arr = new Array([10,20,30]);
console.log(arr)
console.log(arr.constructor);//Array构造函数
console.log(arr.__proto__ === Array.prototype);//true
console.log(arr.__proto__.__proto__.constructor);//Object
console.log(arr.__proto__.__proto__ === Object.prototype);//true
console.log(arr.__proto__.__proto__.__proto__);//null
var str = '123';
//底层用了基本包装类型new String()构造函数
// var str = new String('123');
console.log(str)
console.log(str.constructor);//String构造函数
console.log(str.__proto__ === String.prototype);//true
console.log(str.__proto__.__proto__.constructor);//Object
console.log(str.__proto__.__proto__ === Object.prototype);//true
console.log(str.__proto__.__proto__.__proto__);//null
function fn() {
console.log('fn函数');
}
fn.say = function () {
console.log('函数动态添加方法');
}
fn.show = '函数动态添加属性'
// 下面不报错说明函数也是对象类型 是特殊的对象
fn.say();
console.log(fn.show);
console.log(fn);//打印函数中存储的代码
console.dir(fn);//打印函数对象中存储的属性
console.log(fn.__proto__.constructor);//Function构造函数
//得出函数都是由Function构造函数创建的
console.log(Object.__proto__.constructor);//Function构造函数
console.log(Array.__proto__.constructor);//Function构造函数
console.log(fn.__proto__ === Function.prototype);//true
console.log(fn.__proto__.__proto__.constructor);//Object
console.log(fn.__proto__.__proto__ === Object.prototype);// true
console.log(fn.__proto__.__proto__.__proto__);//null
//函数的两种声明方式
//1.函数声明
function fn(a, b) {
return a + b;
}
//2.表达式声明
var fn1 = function (a, b) {
return a + b;
}
//函数的底层都是new Function
//Function构造函数
//前面所有参数都是函数的形参 最后一个参数是函数体代码
//参数都是字符串
var fn2 = new Function('a', 'b', 'return a+b');
var res = fn2(10, 20);
console.log(res);
//所以js所有构造函数对象 都是Function构造函数生成的
prototype
属性指向与之对应的原型对象constructor
属性指向对应的构造函数__proto__
属性指向对应的构造函数的原型对象//自定义构造函数创建
function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person('张三', 20);
console.log(p);
//内置构造函数创建
//{}底层new Object()
var obj = {}
instanceof
运算符对象 instanceof 构造函数
//判断Object在不在Array的原型链中
//Array函数对象 都是Function创建的
//Array->Function->Object->null
console.log(Array instanceof Object);//true
console.log(Array instanceof Function);//true
console.log(Array instanceof Array);//false
//Function函数对象 都是由Function创建的
//Function->Function->Object->null
console.log(Function instanceof Function);//true
console.log(Function instanceof Object);//true
//Object函数对象 由Function创建
//Object->Function->Object->null
console.log(Object instanceof Function);//true
console.log(Object instanceof Object);//true
//全局函数 this指向window 因为fn全局函数是window的一个属性
function fn() {
console.log(this);//window
}
// window.fn();
fn();
//对象的方法
var p = {
name: '张三',
age: 15,
sayHi() {
console.log(this);//p对象
console.log(this === p);//true
}
}
p.sayHi();
//将fn函数赋值给对象的sayHi
p.sayHi = fn;
p.sayHi();//此时执行fn中代码 this指向p
//构造函数 new会做四件事 会将this指向new创建的对象
function Stu(name, age) {
this.name = name;
this.age = age;
}
var s = new Stu('李四', 18);
console.log(s);
//普通函数 相当于window.Stu()调用 this是window 给window动态加属性
var s1 = Stu('王五', 20);
console.log(s1);//没有返回值 结果undefined
console.log(name);//王五
console.log(age);//20
this谁调用 this指向谁
全局函数中 this指向window
对象方法中 this指向对象
构造函数中 this指向new创建的对象
定时器中 this默认指向window
函数名.call(修改this的指向,arg1,arg2...)
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2);//this指向window
fn.call({ name: '张三' }, 10, 20);//修改后this指向这个对象
函数名.apply(修改this的指向,[arg1,arg2...])
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2);//this指向window
fn.apply([1, 2], [30, 40]);//修改后this指向这个数组
bind()
语法 函数名.apply(修改this的指向,arg1,arg2...)
bind()不会执行这个函数 而是会返回一个修改了this之后的新函数 然后再对这个新函数进行传参
bind ()修改this的指向一般用于回调函数 如定时器
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2);//this指向window
//bind与call不同的是 它不会执行这个函数
//而是会返回一个修改了this之后的函数
//然后在堆返回的函数传参
//bind一般用于回调函数中 如定时器
var fn2 = fn.bind([2, 22, 222]);
fn2(1, 11)
//定时器的this默认指向window 可以用bind修改this的指向
//匿名函数
setTimeout(function () {
console.log(this);
//回调函数中修改this 此时this执行对象
}.bind({}), 2000);
//表达式函数
var test = function () {
console.log(this);
}
setTimeout(test.bind([10, 20]), 1000);
//伪数组转数组
var weiArr = {
0: 10,
1: 20,
2: 30,
length: 3
}
//方法1
var arr =[];
for(var i=0;i<weiArr.length;i++){
arr.push(weiArr[i])
}
console.log(arr);
方法2 声明空数组 通过apply自动遍历伪数组
//方法2
var weiArr = {
0: 10,
1: 20,
2: 30,
length: 3
}
var arr = [];
arr.push.apply(arr, weiArr)
console.log(arr);
方法3 通过数组的slice()方法
传入0 返回数组自身
通过数组的原型调用slice方法
通过call修改this的指向
//方法3
var weiArr = {
0: 10,
1: 20,
2: 30,
length: 3
}
// var res = arr.slice(0);//会返回一个新数组 传入0 返回自身
// console.log(res);
var res = Array.prototype.slice.call(weiArr, 0);
console.log(res);
//伪数组排序
var weiArr = {
0: 20,
1: 1,
2: 5,
3: 2,
4: 10,
length: 5
}
Array.prototype.sort.call(weiArr, function (a, b) {
return a - b;
})
console.log(weiArr);
//数组最大值
//方法1
var arr = [200, 50, 3, 60, 9, 66];
var max = arr[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]
}
}
console.log(max);
方法2 Math.max() 用apply将参数变成数组
//方法2
//Math.max([],[]...) 用apply将参数变成数组
console.log(Math.max.apply(Math,arr));
思考题: 数组调用toString结果与对象调用toString结果为什么不一样??
因为Array的原型中有toString Object的原型中也有toString
根据变量访问原型链成员的构造 如果数组调用toString会访问Array原型中的toString
//Array.prototype.toString() 底层调用的是 数组.join()
//Object.prototype.toString() 返回固定的字符串 [Object 数据类型]
console.log([].toString());//空字符串
console.log({}.toString());//[object Object]
console.log([1,2,3].toString());//1,2,3
console.log({name:'张三'}.toString());//[Object Object]
typeof
关键字检测null和array都会得到object 无法检测console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call([]));//[object Array]
借用Person中的两行代码赋值给Stu函数 通过call修改this为Stu函数的this
只能继承this点出来的属性 如果是实例化点出来的属性无法继承
function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person('张三', 18);
console.log(p);
function Stu(name, age) {
// this.name = name;
// this.age = age;
// Person(name,age);
//借用构造函数继承 通过call修改this为Stu函数中的this
Person.call(this, name, age);
}
var s = new Stu('学生', 18)
console.log(s);
function Person(name, age) {
this.name = name;
this.age = age;
}
//由构造函数点出来的就是静态成员
Person.aaa = 'aaa'
var p = new Person('张三', 18);
//由实例化对象点出来的是实例成员
console.log(p.name);
console.log(p.age);
Object.prototype
所有的对象的原型链最终都会指向Object.prototype
意味着js中所有对象都可以访问Object.prototype中的成员
hasOwnProperty
检测对象是否拥有某个成员对象.hasOwnProperty('属性')
返回值布尔类型 function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.type = '哺乳动物';
var p = new Person('张三', 18);
console.log(p);
//hasOwnProperty 检测对象是否拥有某个成员
//对象.hasOwnProperty('属性') 返回值布尔类型
console.log(p.hasOwnProperty('name'));//true
console.log(p.hasOwnProperty('type'));//false type是原型的 不是p自己的
isPrototypeOf
检测一个对象是不是另一个对象的原型对象a isPrototypeOf(对象b)
检测b的原型是不是a 返回值布尔值console.log(Person.prototype.isPrototypeOf(p));//等价于 p.__proto__ === Person.prototype 结果是true
propertyIsEnumerable
检测某个对象是否可以枚举某个属性(枚举 是自己的成员 可以被forin遍历)对象.propertyIsEnumerable('属性')
// propertyIsEnumerable 检测某个对象是否可以枚举某个属性
//对象.propertyIsEnumerable('属性')
//枚举 是自己的成员 可以被forin遍历
//对象可以被遍历
for(var key in p){
console.log(p[key]);
}
//数组虽然可以被遍历 但是有一个length属性不能被遍历
var arr = [10,20,30];
for(var key in arr){
console.log(arr[key]);
}
console.log(arr.hasOwnProperty('length'));//true 数组中有length属性
console.log(arr.propertyIsEnumerable('length'));//false 数组中length属性不能被遍历 就是不能被枚举 所以false
caller
获取调用这个函数的引用length
获取形参的个数name
获取函数的名字arguments
获取所有的实参
length
获取实参的个数callee
获取函数自身(用于匿名函数递归调用) //函数也是对象
//函数中也有自己默认成员 构造函数中的成员是静态成员
console.dir(fn1)
//caller 获取调用这个函数的引用(我被谁调用了)
//如果函数b中调用函数a 那么函数a的caller就是函数b
//如果是全局调用(window调用) 那么函数的caller就是null
function fn1() {
console.log('哈哈');
console.log(fn1.caller);//fn2
}
function fn2() {
fn1();//fn1的caller就是fn2
console.log('嘿嘿');
}
fn2();
fn1();//如果是全局调用 window调用 那么fn1.caller就是null
//length 获取形参的个数
function fn3(a, b) {
console.log(fn3.length);//2
}
fn3(10, 20, 30, 40)
//name 获取函数的名字
function fn4() {
console.log(fn4.name);//fn4
}
fn4()
//arguments 获取所有的实参
function fn5() {
//arguments伪数组 本质是对象 也有自己的属性callee获取函数自身 length获取实参的个数
console.log(fn5.arguments);
console.log(fn5.arguments.length);//获取实参的个数
console.log(fn5.arguments.callee);//获取函数自身 fn5 用于匿名函数递归调用
console.log(fn5.length);//获取形参的个数
}
fn5(10, 20, 30, 40)
call 是属于Function.prototype 作用是修改函数中this的指向
caller 是属于函数对象的成员 作用是获取调用这个函数的引用(我被谁调用了)
callee 是属于arguments对象的 作用是获取函数本身 应用于匿名函数递归调用
//循环
for(var i =1;i<=3;i++){
fn1()
}
function fn1(){
console.log('循环调用了');
}
//递归
var i = 1;
function fn() {
console.log('递归调用了');
i++;
if (i <= 3) {
fn()
}
}
fn()
function fn1(){
fn2()
}
function fn2(){
fn1()
}
//求1-n的累加和
//循环
function getSum(n) {
var sum = 0;
for (var i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
console.log(getSum(3));
//递归
function getSum(n) {
if (n == 1) {
return 1;
} else {
return getSum(n - 1) + n
}
}
console.log(getSum(3));
//求阶乘
// 1! 1
//2! 2*1 2*getJi(1)
//5! =5*getJi(4)
//循环
function getJi(n) {
var ji = 1;
for (var i = 1; i <= n; i++) {
ji *= i;
}
return ji;
}
console.log(getJi(5));
//递归
function getJi(n) {
if (n == 1) {
return 1;
} else {
return getJi(n - 1) * n
}
}
console.log(getJi(5));
//阶乘的另一种写法
//arguments.callee函数递归调用
var num = (function (n) { return n == 1 ? 1 : arguments.callee(n - 1) * n })(5)
console.log(num);
//斐波那契数列
//1 1 2 3 5 8 13 21 34 ...
//循环
function getFei(n) {
var arr = [1, 1];
for (var i = 2; i < n; i++) {
arr[i] = arr[i - 2] + arr[i - 1]
}
return arr[arr.length - 1]
}
console.log(getFei(9));
//递归
function getFei(n) {
if (n == 1 || n==2) {
return 1;
} else {
return getFei(n - 2) + getFei(n-1)
}
}
console.log(getFei(9));
//获取father父元素的所有后代元素
//遍历父元素 遍历每一个子元素 找到子元素的子元素 继续遍历 以此类推
//声明一个空数组 存储所有的后代元素
var list = [];
function houDai(ele) {
for (var i = 0; i < ele.children.length; i++) {
list.push(ele.children[i]);
//递归求元素的子元素
houDai(ele.children[i])
}
}
houDai(father);
console.log(list);
//获取DOM树
houDai(document);
console.log(list);
function fn() {
var obj = {
name: '张三',
age: 18
}
//要想获取函数中的变量 通过return 值
return obj;
}
//函数外部用变量接收这个返回值
var a = fn();
var b = fn();
//但是多个变量接收的返回值不是同一个对象
console.log(a == b);//false
function fn() {
var obj = {
name: '张三',
age: 18
}
function closure() {
//这里可以访问fn函数中的变量
return obj;
}
//返回闭包函数
return closure;
}
//bibao变量获取的是闭包函数
var bibao = fn();
//闭包函数调用 得到fn中的局部变量
var a = bibao();
var b = bibao();
//多个变量接收的返回值是同一个对象
console.log(a == b);//true
function outer() {
var num = Math.floor(Math.random() * 100)
function closure() {
//这里可以访问outer函数中的变量
return num;
}
//返回闭包函数
return closure;
}
//bibao变量获取的是闭包函数
//调用一次outer函数 访问同一个变量
var bibao = outer();
//闭包函数调用
var a = bibao();
var b = bibao();
console.log(a);
console.log(b);
//调用多次outer函数 访问的是不同的变量
var bibao1 = outer();
var c = bibao1();
console.log(c);
var bibao2 = outer();
var d = bibao2();
console.log(d);
function outer() {
var num = 10;
function closure() {
//这里可以访问outer函数中的变量
num++;
console.log(num);
return num;
}
//返回闭包函数
return closure;
}
//bibao变量获取的是闭包函数
//调用一次outer函数 访问同一个变量
var bibao = outer();
//闭包函数调用 调用closure函数 每调用一次 num++
//相当于一台投票机 投2票
bibao();//11
bibao();//12
//调用多次outer函数 访问的是不同的变量
//相当于 两天投票机 各投一票
var bibao1 = outer();
bibao1();//11
//上面两个式子等同于outer()()
var bibao2 = outer();
bibao2();//11
//点击li元素显示索引
//方法1 原生js
var liList = document.getElementsByTagName('li');
for (var i = 0; i < liList.length; i++) {
liList[i].setAttribute('index', i);
liList[i].onclick = function () {
alert(this.getAttribute('index'))
}
}
//方法2 闭包
var liList = document.getElementsByTagName('li');
for (var i = 0; i < liList.length; i++) {
function outer() {
var num = i;
function closure() {
alert(num)
}
return closure;
}
// var bibao = outer();
// liList[i].onclick = bibao;
//简写
liList[i].onclick = outer()
}
//开3个定时器 1s后分别打印1 2 3
//定时器中的代码不是立即执行的 而是等页面所有代码加载完毕 1s之后才执行 此时循环早已走完
//方法1
for (let i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i);
}, 1000)
}
//方法2
for (var i = 1; i <= 3; i++) {
function outer() {
var num = i;
setTimeout(function () {
console.log(num);
}, 1000)
function closure() {
return num
}
return closure;
}
outer()
}
//方法2简写
for (var i = 1; i <= 3; i++) {
function outer() {
var num = i;
return function closure() {
console.log(num);
}
}
setTimeout(outer(), 1000)
}
//斐波那契数列
//原生 循环(数组中性能稍差 需要存储多个元素 浪费内存)
function fib(n) {
var arr = [1, 1];
for (var i = 2; i < n; i++) {
arr[i] = arr[i - 2] + arr[i - 1]
}
return arr[arr.length - 1]
}
var res = fib(10);
console.log(res);
//递归(性能差 需要重复计算)
function getFib(n) {
if (n == 1 || n == 2) {
return 1;
} else {
return getFib(n - 2) + getFib(n - 1)
}
}
var res = getFib(10);
console.log(res);
//第三个元素占位空间 存储最终的结果
//每调用一次fib() 内部数组都要重新声明 所以此时要使用闭包延长数组的生命周期
function fib(n) {
var arr = [1, 1, 0];
for (var i = 2; i < n; i++) {
arr[2] = arr[0] + arr[1];
arr[0] = arr[1];
arr[1] = arr[2];
}
return arr[2]
}
console.log(fib(10));
//闭包
function fib() {
var arr = [1, 1, 0];
var closure = function(n){
for (var i = 2; i < n; i++) {
arr[2] = arr[0] + arr[1];
arr[0] = arr[1];
arr[1] = arr[2];
}
return arr[2]
}
return closure;
}
var bibao = fib();
console.log(bibao(10));
//但是 再次调用需要释放闭包的内存
bibao = null;
var bibao = fib();
console.log(bibao(10));
var name = 'My Window';
var obj = {
name: 'My Object',
getFunction() {
//返回是到全局中 所以this指向window
return function () {
console.log(this.name);//My Window
}
}
}
obj.getFunction()();//My Object
var name = 'My Window';
var obj = {
name: 'My Object',
getFunction() {
//方法的this是obj 所以that是obj对象
//闭包会延长that的生命周期
var that = this;
return function () {
console.log(that.name);//My Object
}
}
}
obj.getFunction()();