面向对象-高级

1.构造函数  bind   面向对象,点击按钮,改变样式

/*
*   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()
})()
View Code

2.贪吃蛇思路

<!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>
View Code

 3. 原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的

    组合继承、不使用多态、拷贝继承

/*
* 面向对象的特性:封装,继承,多态
    封装;就是代码的封装,把一些特征和行为封装在对象中.
*
*   //原型指向可以改变
    //实例对象的__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 是对象,不是函数
View Code

 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]'  判断数组

/*  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("这个函数,执行了");
}
View Code

 5.

// 闭包, 沙箱:自调用函数, 递归(不实用)递归轻易不要用,效率很低,
// 浅拷贝 extend() 常用
// 深拷贝 更麻烦 用了递归,效率低,不实用
// 闭包, 沙箱:自调用函数, 递归(不实用)递归轻易不要用,效率很低,
// 浅拷贝 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)
View Code

 

posted @ 2018-12-03 10:54  gyz418  阅读(166)  评论(2编辑  收藏  举报