面向对象-高级
1.构造函数 bind 面向对象,点击按钮,改变样式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * js基本类型 * * * 基本数据类型:在内存中占据固定大小的空间,被保存在栈内存中,值存储 * Undefined- Null- Boolean- Number- String * * 引用类型:保存在堆内存,引用存储 * Object- Array * 从一个变量向另一个变量复制引用类型的值的时候,复制是引用指针,因此两个变量最终都指向同一个对象 * * Object.prototype.toString.call() * * * JavaScript 执行过程 * 预解析:全局预解析(所有变量和函数声明都会提前;同名的函数和变量函数的优先级高) * 函数内部预解析(所有的变量、函数和形参都会参与预解析) * 执行:先预解析全局作用域,然后执行全局作用域中的代码, 在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内代码。 * * * 多态:同一个行为,针对不同的对象,产生了不同的效果 * * */ // 1. 对象字面量 只能创建一个 var per1 = { name: '卡卡西', eat: function () { console.log(this.name) } } // 2. 调用系统的构造函数 // 不知道这个对象的类型 var per2 = new Object() per2.name = '大蛇丸' per2.eat = function () { console.log(this.per2) } // 3.工厂模式 函数名小写 function createObject (name, age) { var obj = new Object() obj.name = name obj.age = age obj.sayHi = function () { console.log('您好') } return obj } // var per2=createObject("小明",20); // 4 . 自定义构造函数 /* * 1.开辟空间存储对象 * 2.把this设置为当前的对象 * 3.设置属性和方法的值 * 4.把this对象返回 */ function Person (name, age, sex) { this.name = name this.age = age this.sex = sex this.play = function () { console.log('天天打游戏') } } var per = new Person('雏田', 18, '女') per.play() console.log(per instanceof Person) // true 上两种不知道类型 console.log(per.constructor === Person)// true console.log(per.__proto__.constructor === Person) // true console.log(per.__proto__.constructor === Person.prototype.constructor) // true var per2 = new Person('雏田2', 18, '女') per2.play() console.log(per.play == per2.play) // false 构造函数,每个对象都有自己的方法,占用不同空间,浪费内存 // 5 通过原型来解决---------数据共享,节省内存空间,作用之一 // 解决 构造函数的资源共享 function Stu (name, age) { this.name = name this.age = age } //通过原型来添加方法,解决数据共享,节省内存空间 // 把eat() 放到 p1.__proto__ 里面去了,共享 // 如果不共享,就不用写到 prototype里面去 /* * 实例对象中有__proto__这个属性,叫原型,也是一个对象,这个属性是给浏览器使用,不是标准的属性 * 构造函数中有prototype这个属性,叫原型,也是一个对象,这个属性是给程序员使用,是标准的属性 * 实例对象的__proto__和构造函数中的prototype相等--->true * 又因为实例对象是通过构造函数来创建的,构造函数中有原型对象prototype * 实例对象的__proto__指向了构造函数的原型对象prototype * * * * 构造函数可以实例化对象 * 构造函数中有一个属性叫prototype,是构造函数的原型对象 * 构造函数的原型对象(prototype)中有一个constructor构造器,这个构造器指向的就是自己所在的原型对象所在的构造函数 * 实例对象的原型对象(__proto__)指向的是该构造函数的原型对象 * 构造函数的原型对象(prototype)中的方法是可以被实例对象直接访问的 * * */ Stu.prototype.eat = function () { console.log('吃凉菜') } var p1 = new Stu('小明', 20) var p2 = new Stu('小红', 30) console.log(p1.eat == p2.eat)//true console.dir(p1) console.dir(p2) /* * 面向对象,点击按钮,改变样式 * * */ function ChangeStyle (btnObj, dvObj, json) { this.btnObj = btnObj this.dvObj = dvObj this.json = json } ChangeStyle.prototype.init = function () { //点击按钮,改变div多个样式属性值 var that = this this.btnObj.onclick = function () {//按钮 for (var key in that.json) { that.dvObj.style[key] = that.json[key] } } } function my$ (id) { return document.getElementById(id) } //实例化对象 var json = {'width': '500px', 'height': '800px', 'backgroundColor': 'blue', 'opacity': '0.2'} // var cs = new ChangeStyle(my$("btn"), my$("dv"), json); // cs.init();//调用方法 // 传统 // document.getElementById("btn").onclick=function () { // document.getElementById("dv").style.backgroundColor="yellow"; // }; //什么样子的数据是需要写在原型中? //需要共享的数据就可以写原型中 //原型的作用之一:数据共享 //属性需要共享,方法也需要共享 //不需要共享的数据写在构造函数中,需要共享的数据写在原型中 //构造函数 function Student (name, age, sex) { this.name = name this.age = age this.sex = sex } //所有学生的身高都是188,所有人的体重都是55 //所有学生都要每天写500行代码 //所有学生每天都要吃一个10斤的西瓜 //原型对象 Student.prototype.height = '188' Student.prototype.weight = '55kg' Student.prototype.study = function () { console.log('学习,写500行代码,小菜一碟') } Student.prototype.eat = function () { console.log('吃一个10斤的西瓜') } //实例化对象,并初始化 var stu00 = new Student('晨光', 57, '女') console.dir(Student) console.dir(stu00) function Student30 (age) { this.age = age } // 构造器丢失问题 /* Student.prototype.eat=function(){ // 这种写法有构造器 console.log('eat') }*/ Student30.prototype = { constructor: Student30, // 手动添加构造器,这种写法会丢失构造器 eat: function () { console.log('eat') } } let stu = new Student30(12) console.log(stu) //原型对象中的方法,可以相互调用 //原型中添加方法 function Animal (name, age) { this.name = name this.age = age } Animal.prototype.eat = function () { console.log('动物吃东西') this.play() } Animal.prototype.play = function () { console.log('玩球') } /* * 实例对象使用的属性或者方法,先在实例中查找,找到了则直接使用,找不到则,去实例对象的__proto__指向的原型对象prototype中找,找到了则使用,找不到则报错 * */ function Person20 (age, sex) { this.age = age//年龄 this.sex = sex this.eat = function () { console.log('构造函数中的吃') } } Person20.prototype.sex = '女' Person20.prototype.eat = function () { console.log('原型对象中的吃') } var per20 = new Person20(20, '男') console.log(per20.sex)//男 per20.eat() console.dir(per20); (function (win) { var num = 10//局部变量 //js是一门动态类型的语言,对象没有属性,点了就有了 win.num = num })(window) console.log(num) // bind 改变this的指向 let name = 'kang' setTimeout(function () { console.log(this.name) // kang // 默认 this指window }, 1000) (function () { let that = null // 谁调用了bind, this就被传入的值替换掉 function Person (name) { this.name = name that = this // 用变量保存了this this.init = function () { setTimeout(function () { console.log(this.name) }.bind(that), 1000) // 'haha' } } new Person('haha').init() })()
2.贪吃蛇思路
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>snake1</title> </head> <style> .map { width: 800px; height: 600px; background-color: #CCC; position: relative; } </style> <body> <div class="map"></div> <script> /* * 贪吃蛇思路 * 1. (function(){})(); 一个js 一个自调用函数 * 2. 蛇是一个对象, 食物是一个对象 * 3. 蛇移动代码太多,又封装成一个游戏对象 * 4. 食物的产生 * 5. 蛇 * 5.1 蛇的产生(蛇身是由多段组成的) * 5.2 蛇的移动 * 5.2.1定时器,再删除原来的蛇 * 5.2.2新蛇的蛇身变化(坐标变化)、蛇头由方向盘变化而变化(坐标变化) * 6 蛇撞墙判断 * 7 当坐标一致时,蛇吃食物,尾巴变长(this.body=[] 数据追加一个蛇尾), 增加新食物 * 8 fn().bind(that) 函数里面的this被 bind的that替换掉 * * * * * */ // 食物 (function () { var elements = [] // 放食物?方便删除 // 食物 function Food (x, y, width, height, color) { this.width = width || 20 this.height = height || 20 this.x = x || 0//横坐标随机产生的 this.y = y || 0//纵坐标随机产生的 this.color = color || 'green' } // init Food.prototype.init = function (map) { // 删除元素,防止多次init 生成多个食物 remove() //设置小方块的样式 var div = document.createElement('div') //把小方块加到map地图中 map.appendChild(div) div.style.width = this.width + 'px' div.style.height = this.height + 'px' div.style.borderRadius='50%' div.style.backgroundColor = this.color div.style.position = 'absolute' // 先除再乘,保证方块的坐标是宽高的整数倍 this.x = parseInt(Math.random() * (map.offsetWidth / this.width)) * this.width this.y = parseInt(Math.random() * (map.offsetHeight / this.height)) * this.height div.style.left = this.x + 'px' div.style.top = this.y + 'px' elements.push(div) // 存放的是 div 这个元素 } // 删除 私有函数。。 function remove () { // /* * new Food().init(document.querySelector('.map')) * 单个new remove() 用不到 * * */ for (var i = 0; i < elements.length; i++) { let ele = elements[i] ele.parentNode.removeChild(ele) // 通过父元素来删除 dom节点 elements.splice(i, 1) // 清掉数组里面的值 } console.log(elements) } window.Food = Food })(); // 蛇 (function () { var elements = [] // 存放蛇身体 function Snake (width, height, direction) { // 蛇每个部分的宽 this.width = width || 20 this.height = height || 20 // 蛇的身体 // 直接弄个 this.body 出来,不需要形参 this.body = [ {x: 3, y: 2, color: 'red'}, // 头 {x: 2, y: 2, color: 'orange'}, // 身体 {x: 1, y: 2, color: 'orange'} // 身体 ] this.direction = direction || 'right' // 方向 } // init Snake.prototype.init = function (map) { // 删除旧的蛇; remove() // 一条蛇由身体各个部位(div)组成 for (let i = 0; i < this.body.length; i++) { let obj = this.body[i] let div = document.createElement('div') map.appendChild(div) div.style.position = 'absolute' div.style.width = this.width + 'px' div.style.borderRadius='50%' div.style.height = this.height + 'px' // 横纵坐标 div.style.left = obj.x * this.width + 'px' div.style.top = obj.y * this.width + 'px' div.style.backgroundColor = obj.color // 方便删除 elements.push(div) } } // 蛇移动 Snake.prototype.move = function (food, map) { var i = this.body.length - 1 // i = 2 // 改蛇身 for (; i > 0; i--) { this.body[i].x = this.body[i - 1].x // 蛇身的最后一段是上一段的值 this.body[i].y = this.body[i - 1].y // } // 方向 蛇头 switch (this.direction) { case 'right': this.body[0].x += 1 break case 'left': this.body[0].x -= 1 break case 'top': this.body[0].y -= 1 break case 'bottom': this.body[0].y += 1 break } // 蛇坐标 var headX = this.body[0].x * this.width var headY = this.body[0].y * this.height // 小蛇头的坐标和食物坐标一致则吃到食物 if (headX === food.x && headY === food.y) { // 蛇尾加长 var last = this.body[this.body.length - 1] console.log('last',last) this.body.push({ x:last.x, y:last.y, color:last.color }) // 删除旧食物,产生新的食物 food.init(map) } } // 删除蛇 function remove () { var i = elements.length - 1 // 从蛇尾开始删 for (; i >= 0; i--) { var ele = elements[i] ele.parentNode.removeChild(ele) elements.splice(i, 1) } } window.Snake = Snake })(); // 游戏 /* * 游戏启动需要调用食物、调用蛇、并移动,步骤太多,于是,又封装成一个游戏对象 * * */ (function () { let that = null function Game (map) { this.food = new Food() // 直接 实例化,可以拿到属性和方法 this.snake = new Snake() this.map = map that = this // 直接写个变量 window.that = this } // init Game.prototype.init = function () { this.food.init(this.map) // 食物 this.snake.init(this.map) // 蛇 /* setInterval(() => { this.snake.move() this.snake.init(this.map) }, 1000)*/ this.runSnake(this.food, this.map) this.bindKey() } // 蛇自由移动 Game.prototype.runSnake = function (food, map) { let interval = setInterval(() => { // 坐标最大值 var maxX = map.offsetWidth / this.snake.width var maxY = map.offsetHeight / this.snake.height // 蛇坐标 let snakeX = this.snake.body[0].x let snakeY = this.snake.body[0].y // 撞墙 if (snakeX === 0 || snakeX >= maxX - 1) { window.clearInterval(interval) alert('game over') /* setTimeout(()=>{ startAgain() },5000) */ return } if (snakeY === 0 || snakeY >= maxY - 1) { window.clearInterval(interval) alert('game over') /*setTimeout(()=>{ startAgain() },5000)*/ return } this.snake.move(food, map) this.snake.init(map) }, 150) } // 按键移动 Game.prototype.bindKey = function () { // document.onkeydown=function (e) { document.addEventListener('keydown', function (e) { // this switch (e.keyCode) { case 37: this.snake.direction = 'left' break case 38: this.snake.direction = 'top' break case 39: this.snake.direction = 'right' break case 40: this.snake.direction = 'bottom' break } }.bind(that), false) // bind 改变this 指向 fn().bind(that) } window.Game = Game; })() // var gm = new Game(document.querySelector('.map')) // gm.init() startAgain(); function startAgain(){ var gm = new Game(document.querySelector('.map')) gm.init() } // 显示食物 // new Food().init(document.querySelector('.map')) // 显示 n 条蛇 ,移动后,删除旧的蛇 // let snake = new Snake() // snake.init(document.querySelector('.map')) // setInterval(() => { // snake.move() // snake.init(document.querySelector('.map')) // }, 1000) </script> </body> </html>
3. 原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的
组合继承、不使用多态、拷贝继承
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * 面向对象的特性:封装,继承,多态 封装;就是代码的封装,把一些特征和行为封装在对象中. * * //原型指向可以改变 //实例对象的__proto__原型指向的是该对象所在的构造函数的原型对象prototype //构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(__proto__)指向也会发生改变 * * 原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的 * * 构造函数中的this就是实例对象 * 原型对象中方法中的this就是实例对象 * */ //人的构造函数 function Person(age) { this.age=10; } //人的原型对象方法 Person.prototype.eat=function () { console.log("人的吃"); }; //学生的构造函数 function Student() { } Student.prototype.sayHi=function () { console.log("嗨,小苏你好帅哦"); }; //学生的原型,指向了一个人的实例对象 Student.prototype=new Person(10); var stu=new Student(); stu.eat(); // stu.sayHi(); //如果原型指向改变了,那么就应该在原型改变指向之后添加原型方法 // 先改变指向,再添加原型方法 function Person2(age) { this.age = age; } //指向改变了 Person2.prototype = { // 类似 Person2.prototype= new Stu(); eat: function () { console.log("吃"); } }; //先添加原型方法 Person2.prototype.sayHi = function () { console.log("您好"); }; var per = new Person2(10); per.sayHi(); console.dir(Person2) /* 继承 */ /* * JS是一门基于对象的语言, * 继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承 * 继承也是为了数据共享,js中的继承也是为了实现数据共享 * 原型作用之一:数据共享,节省内存空间 * 原型作用之二:为了实现继承 * * 多态:一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果,要想有多态,就要先有继承,js中可以模拟多态,但是不会去使用,也不会模拟, * */ //js中通过原型来实现继承,但多个实例对象的继承属性、方法都一致了 function Person3(name,age,sex) { this.name=name; this.sex=sex; this.age=age; } Person3.prototype.eat=function () { console.log("人可以吃东西"); }; Person3.prototype.sleep=function () { console.log("人在睡觉"); }; Person3.prototype.play=function () { console.log("生活就是不一样的玩法而已"); }; function Student3(score) { this.score=score; } //改变学生的原型的指向即可==========>学生和人已经发生关系 继承基本属性,方法 Student3.prototype=new Person3("小明",10,"男"); Student3.prototype.study=function () { console.log("学习很累很累的哦."); }; //相同的代码太多,造成了代码的冗余(重复的代码) var stu3=new Student3(100); console.log(stu3.name); console.log(stu3.age); console.log(stu3.sex); stu3.eat(); stu3.play(); stu3.sleep(); console.log("下面的是学生对象中自己有的"); console.log(stu3.score); stu3.study(); /* * 借用构造函数,只继承属性 * * */ //借用构造函数:构造函数名字.call(当前对象,属性,属性,属性....); //解决了属性继承,并且值不重复的问题 //缺陷:父级类别中的方法不能继承 function Person4(name, age, sex, weight) { this.name = name; this.age = age; this.sex = sex; this.weight = weight; } Person4.prototype.sayHi = function () { console.log("您好"); }; function Student4(name,age,sex,weight,score) { //借用构造函数 Person4.call(this,name,age,sex,weight); // 不借用构造函数的话,名字,年龄数据都一样了 this.score = score; } var stu1 = new Student4("小明",10,"男","10kg","100"); console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score); var stu2 = new Student4("小红",20,"女","20kg","120"); console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score); var stu3 = new Student4("小丽",30,"妖","30kg","130"); console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score); /* * 组合继承 :原型继承+借用构造函数继承 继承属性和方法 * * */ function Person5(name,age,sex) { this.name=name; this.age=age; this.sex=sex; } Person5.prototype.sayHi=function () { console.log("阿涅哈斯诶呦"); }; function Student5(name,age,sex,score) { //借用构造函数:属性值重复的问题 Person5.call(this,name,age,sex); this.score=score; } //改变原型指向----继承 Student5.prototype=new Person5();//不传值 Student5.prototype.eat=function () { console.log("吃东西"); }; var stu5=new Student5("小黑",20,"男","100分"); console.log(stu5.name,stu5.age,stu5.sex,stu5.score); stu5.sayHi(); stu5.eat(); var stu55=new Student5("小黑黑",200,"男人","1010分"); console.log(stu55.name,stu55.age,stu55.sex,stu55.score); stu55.sayHi(); stu55.eat(); /* * * 拷贝继承;就是把对象中需要【共享】的属性或者方法,直接遍历的方式复制到另一个对象中 * * */ // var obj1={ // name:"小糊涂", // age:20, // sleep:function () { // console.log("睡觉了"); // } // }; // // //改变了地址的指向 改变对象的地址指向,不算拷贝 // var obj2=obj1; // console.log(obj2.name,obj2.age); // obj2.sleep(); // var obj1={ // name:"小糊涂", // age:20, // sleep:function () { // console.log("睡觉了"); // } // }; // // // var obj2={}; 真正拷贝 浅拷贝,复制堆内存中的属性,多复制一份 // for(var key in obj1){ // obj2[key]=obj1[key]; // } // console.log(obj2.name); function Person6() { } Person6.prototype.age=10; Person6.prototype.sex="男"; Person6.prototype.height=100; Person6.prototype.play=function () { console.log("玩的好开心"); }; var obj2={}; //Person的构造中有原型prototype,prototype就是一个对象,那么里面,age,sex,height,play都是该对象中的属性或者方法 for(var key in Person6.prototype){ obj2[key]=Person6.prototype[key]; } console.dir(obj2); obj2.play(); /* * * 函数声明bug, 函数声明,代码会提前执行,放到if else 会提前声明,ie8出错 * * * */ //函数声明 // // if(true){ // function f1() { // console.log("哈哈,我又变帅了"); // } // }else{ // function f1() { // console.log("小苏好猥琐"); // } // } // f1(); //函数表达式 var ff; if(true){ ff=function () { console.log("哈哈,我又变帅了"); }; }else{ ff=function () { console.log("小苏好猥琐"); }; } ff(); //函数声明如果放在if-else的语句中,在IE8的浏览器中会出现问题 //以后宁愿用函数表达式,都不用函数声明 // 严格模式: "use strict";//严格模式 function f1() { console.log(this);//window } f1(); // undefined 严格模式下,必须指明 window.f1(); // 对象中有__proto__,函数中应该有prototype Math 是对象,不是函数
4.apply call bind 三者都可以改变this的指向,使用箭头函数就避开了。 函数作为返回值 排序 film.sort('name')
f().apply(obj,[]) f().call(obj,val,val) f().bind(obj,val,val)
Object.prototype.toString.call(arr) === '[object Array]' 判断数组
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* apply call bind 三者都可以改变this的指向,使用箭头函数就避开了。*/ /* * apply call 可以改变this的指向 * apply和call方法中如果没有传入参数,或者是传入的是null,那么调用该方法的函数对象中的this就是默认的window * * f().apply(obj,[]) * f().call(obj,val,val) * */ function f1 (x, y) { console.log(x + y, this) return x + y + 1000 } f1.apply() f1.call(null) //两者不传参数,或者传null this 都是 window let res1 = f1.apply(null, [100, 200]) // 传数组 let res2 = f1.call(null, 100, 200) // 传变量 console.log(res1) // 普通的函数返回值 console.log(res2) function Person (age, sex) { this.age = age this.sex = sex } //通过原型添加方法 Person.prototype.sayHi = function (x, y) { console.log('您好啊:' + this.sex) return 1000 } var per = new Person(10, '男') per.sayHi() console.log('==============') function Student (name, sex) { this.name = name this.sex = sex } var stu = new Student('小明', '人妖') var r1 = per.sayHi.apply(stu, [10, 20]) // 改变this指向 stu替换掉 per了 var r2 = per.sayHi.call(stu, 10, 20) console.log(r1) console.log(r2) /* * bind 复制,参数可以在复制的时候传进去,也可以在复制之后调用的时候传入进去 * 改变this 指向 * * * */ console.log('-----------------bind-------------') function f (x, y) { console.log(x + y, this.age) return x + y } var ff = f.bind(null) ff(10, 20) // 复制后传参数 function Person2 () { this.age = 10 } Person2.prototype.eat = function () { console.log('eat') } var per2 = new Person2() var ff2 = f.bind(per2, 10, 200) // 直接传参数 ff2() function Person3 (age) { this.age = age } Person3.prototype.play = function () { console.log(this + '====>' + this.age) } function Student3 (age) { this.age = age } var per3 = new Person3(10) var stu3 = new Student3(20) //复制了一份 var ff3 = per3.play.bind(stu3) // 返回值是复制之后的这个方法 ff3() //函数中有一个name属性----->函数的名字,name属性是只读的,不能修改 //函数中有一个arguments属性--->实参的个数 //函数中有一个length属性---->函数定义的时候形参的个数 //函数中有一个caller属性---->调用(f1函数在f2函数中调用的,所以,此时调用者就是f2) function f1 (x, y) { console.log(f1.name) console.log(f1.arguments.length) console.log(f1.length) console.log(f1.caller)//调用者 } // f1.name="f5"; // f1(10,20,30,40); // console.dir(f1); function f2 () { console.log('f2函数的代码') f1(1, 2) } f2() //输出的数组的数据类型 [object Array] // console.log(Object.prototype.toString.call([])); // console.log(Object.prototype.toString()==='[object object]') // true var arr = [10, 20, 30] // 判断是不是数组 console.log(Object.prototype.toString.call(arr) === '[object Array]') // true let obj5 = {} console.log(typeof obj5) // object console.log(Object.prototype.toString.call(obj5)) // [object object] let arr5 = [] console.log(typeof arr5) // object console.log(Object.prototype.toString.call(arr5)) // [object Array] /* call apply bind 可通用 bind 返回值是函数,需要再调用一下才有结果 */ console.log(Object.prototype.toString.apply(arr5)) // [object Array] console.log(Object.prototype.toString.bind(arr5)()) // [object Array] bind 返回值是一个方法,要再调用一下 console.log(Object.prototype.toString.apply(new Date())) // [object Date] let date = new Date() console.log(typeof date) // object console.log( date instanceof Date) //判断这个对象和传入的类型是不是同一个类型 /* * 函数作为返回值 * * */ function getFunc(type) { return function (obj) { return Object.prototype.toString.call(obj) === type; } } var ff6 = getFunc("[object Array]"); var result = ff6([10, 20, 30]); // 传两次参数 console.log(result); var arr = [1, 100, 20, 200, 40, 50, 120, 10]; //排序---函数作为参数使用,匿名函数作为sort方法的参数使用,那么此时的匿名函数中有两个参数, arr.sort(function (obj1,obj2) { if(obj1>obj2){ return -1; // return 1; 在这里切换 从小到大排序 或者从大到小排序 }else if(obj1===obj2){ return 0; }else{ return 1; // return -1; } }); console.log(arr); var arr1=["acdef","abcd","bcedf","bced"]; arr1.sort(function (a,b) { if(a>b){ return 1; }else if(a===b){ return 0; }else{ return -1; } }); console.log(arr1); /* 函数作为返回值应用 **/ function File (name, size, time) { this.name = name;//电影名字 this.size = size;//电影大小 this.time = time;//电影的上映时间 } var f1 = new File("jack.avi", "400M", "1997-12-12"); var f2 = new File("tom.avi", "200M", "2017-12-12"); var f3 = new File("xiaosu.avi", "800M", "2010-12-12"); var arr = [f1,f2,f3] // 放到数组是为了 arr.sort() function getSort (obj1, obj2) { if (obj1.name > obj2.name) { return 1; } else if (obj1.name === obj2.name) { return 0; } else { return -1; } } arr.sort(getSort) for (var i = 0; i < arr.length; i++) { console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time); } /* 以上写法只能按name排序,函数作为返回值,调两次函数,一次比较,一次改比较的值 */ function fn (attr) { // 函数作为返回值 return function getSort (obj1, obj2) { if (obj1[attr] > obj2[attr]) { return 1; } else if (obj1[attr] === obj2[attr]) { return 0; } else { return -1; } } } let f100 = fn('size') arr.sort(f100) for (var i = 0; i < arr.length; i++) { console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time); } /* sort 3 */ let filmArr=[ {name:'jack.avi',size:'400M',time:'1997-12-12'}, {name:'tom.avi',size:'200M',time:'2017-12-12'}, {name:'xiaosu.avi',size:'800M',time:'2010-12-12'}, ] let film= fn('time') //函数作为参数 filmArr.sort(film) filmArr.forEach(val=>{ console.log('film',val.name,val.size,val.time) }) // 作用域链:变量的使用,从里向外,层层的搜索,搜索到了就可以直接使用了 // 层层搜索,搜索到0级作用域的时候,如果还是没有找到这个变量,结果就是报错 var num=10; //作用域链 级别:0 var str = "abc" function f111() { var num=20; // 作用域链 级别 1 function f2() { var num=30; // 作用域链 级别 2 console.log(num); // 30 } f2(); } f111(); //预解析:就是在浏览器解析代码之前,把【变量的声明】和函数的声明提前(提升)到该作用域的最上面 //变量的提升 console.log(num2000); // 变量存在,但未赋值 var num2000=100; //函数的声明被提前了 f1000(); function f1000() { console.log("这个函数,执行了"); }
5.
// 闭包, 沙箱:自调用函数, 递归(不实用)递归轻易不要用,效率很低,
// 浅拷贝 extend() 常用
// 深拷贝 更麻烦 用了递归,效率低,不实用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 闭包, 沙箱:自调用函数, 递归(不实用)递归轻易不要用,效率很低, // 浅拷贝 extend() 常用 // 深拷贝 更麻烦 用了递归,效率低,不实用 /* * * 闭包的概念:函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成了闭包 * 闭包的模式:函数模式的闭包,对象模式的闭包 * 闭包的作用:缓存数据,延长作用域链 * 闭包的优点和缺点:缓存数据 * * */ // 函数模式的闭包:在一个函数中有一个函数, 两个函数都要调用 function f1 () { var num = 10 //函数的声明 function f2 () { console.log(num) // 10 } //函数调用 f2() } f1() //对象模式的闭包:函数中有一个对象 function f3 () { var num = 11 var obj = { age: num } console.log(obj.age)//10 } f3() // 函数闭包 返回一个函数,函数里再返回 function f4 () { var num = 12 return function () { console.log(num) return num } } var f44 = f4() console.log(f44()) // 函数闭包 返回一个对象 function f5 () { var num = 14; return { age:num } } let obj = f5() console.log('age',obj.age) // //函数模式的闭包 function f6() { var num = 10; return function () { num++; return num; } } var ff = f6(); // 返回一个函数:匿名函数 console.log(ff());//11 返回的匿名函数进行调用 console.log(ff());//12 返回的匿名函数进行调用 console.log(ff());//13 返回的匿名函数进行调用 //闭包的方式,产生三个随机数,但是都是相同的 function f7() { var num=parseInt(Math.random()*10+1); return function () { console.log(num); } } var ff7=f7(); ff7(); ff7(); ff7(); //总结:如果想要缓存数据,就把这个数据放在外层的函数和里层的函数的中间位置 //闭包的作用:缓存数据.优点也是缺陷,没有及时的释放 //局部变量是在函数中,函数使用结束后,局部变量就会被自动的释放 //闭包后,里面的局部变量的使用作用域链就会被延长 // 闭包应用 点赞 /* * <li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li> <li><img src="images/lyml.jpg" alt=""><br/><input type="button" value="赞(1)"></li> * */ function my$(tagName) { return document.getElementsByTagName(tagName); } //闭包缓存数据 function getValue() { var value=2; return function () { //每一次点击的时候,都应该改变当前点击按钮的value值 this.value="赞("+(value++)+")"; } } /* //获取所有的按钮 var btnObjs=my$("input"); //循环遍历每个按钮,注册点击事件 for(var i=0;i<btnObjs.length;i++){ //注册事件 btnObjs[i].onclick=getValue(); // 调用后 getValue() 返回一个匿名函数 btn.onclick=function(){} } */ //沙箱:环境,黑盒,在一个虚拟的环境中模拟真实世界,做实验,实验结果和真实世界的结果是一样,但是不会影响真实世界 var day = 1; (function () { // 这就是沙箱,不受外面代码影响 var day = 2; console.log(day) // 不跟外界冲突 })(); /* 递归:函数中调用函数自己,此时就是递归,递归一定要有结束的条件 */ var i = 0; function f77() { i++; if (i < 5) { f77(); } console.log("从前有个山,山里有个庙,庙里有个和尚给小和尚讲故事:"); } f77(); //递归实现:求n个数字的和 n=5---> 5+4+3+2+1 //函数的声明 function getSum(x) { if(x===1){ return 1; } return x+getSum(x-1); } //函数的调用 console.log(getSum(5)); //递归案例:求一个数字各个位数上的数字的和: 123 --->6 ---1+2+3 function getEverySum(x) { if(x<10){ return x; } //获取的是这个数字的个位数 return x%10+getEverySum(parseInt(x/10)); } console.log(getEverySum(1364));//14 //递归案例:求斐波那契数列 function getFib(x) { if(x==1||x==2){ return 1 } return getFib(x-1)+getFib(x-2); } console.log(getFib(12)); // 1 1 2 3 5 8 13 var obj1={ age:10, sex:"男", car:["奔驰","宝马","特斯拉","奥拓"] }; //另一个对象 var obj2={}; //写一个函数,作用:把一个对象的属性复制到另一个对象中,浅拷贝 //把a对象中的所有的属性复制到对象b中 function extend(a,b) { for(var key in a){ b[key]=a[key]; } } extend(obj1,obj2); console.dir(obj2);//开始的时候这个对象是空对象 console.dir(obj1);//有属性 //深拷贝:拷贝还是复制,深:把一个对象中所有的属性或者方法,一个一个的找到.并且在另一个对象中开辟相应的空间,一个一个的存储到另一个对象中 // 【不实用,结果跟浅拷贝一样,多占了一堆空间】 var obj1111={ age:10, sex:"男", car:["奔驰","宝马","特斯拉","奥拓"], dog:{ name:"大黄", age:5, color:"黑白色" } }; var obj2222={};//空对象 //通过函数实现,把对象a中的所有的数据深拷贝到对象b中 function extend111(a,b) { for(var key in a){ //先获取a对象中每个属性的值 var item=a[key]; //判断这个属性的值是不是数组 if(item instanceof Array){ //如果是数组,那么在b对象中添加一个新的属性,并且这个属性值也是数组 b[key]=[]; //调用这个方法,把a对象中这个数组的属性值一个一个的复制到b对象的这个数组属性中 extend(item,b[key]); }else if(item instanceof Object){//判断这个值是不是对象类型的 //如果是对象类型的,那么在b对象中添加一个属性,是一个空对象 b[key]={}; //再次调用这个函数,把a对象中的属性对象的值一个一个的复制到b对象的这个属性对象中 extend(item,b[key]); }else{ //如果值是普通的数据,直接复制到b对象的这个属性中 b[key]=item; } } } extend111(obj1111,obj2222); console.dir(obj1111); console.dir(obj2222); // 递归找子节点 function getNode (root) { // console.log(root) // 获得所有html源码 let children = root.children for(var i =0;i<children.length;i++){ let child = children[i] console.log(child.nodeName) // child.children.length && getNode(child) // 如果有子节点,递归,再找它的子节点 if(child.children.length){ getNode(child) }else{ console.log('想干嘛都行') } } } // getNode(document.documentElement)