深入理解JavaScript高级

JavaScript进阶

变量类型

tyoeof:
typeof undefined  //undefined
typeof 'abc' //string
typeof 123 //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function
==:
100='100' //true
0 == '' //true
null == undefined //true
使用==||===:

推荐:出了下面这个其他地方全部用三等

if(obj.a==null){//相当于obj.a===null || obj===undefined这个的简写形式
	
}
内置函数有:

Object Array Boolean Number String Function Date RegExp Error Math

创建对象:

  1. 创建一个新对象
  2. this指向新对象
  3. 执行代码,赋值等运算
  4. 返回this
  5. 如果构造函数返回一个对象,那么这个对象会取代整个new出来的结果。那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象
1、字面量创建

全为静态成员,适用于tools类等

var MyMath = {
      PI: 3.1415926,
      max: function () {  },
      min: function () {  }
    } 
console.log(MyMath.PI);
2、工厂方法创建对象

创建对象为Object的对象,而不是一个hero的对象

//  工厂函数创建对象 
    function createHero(name, blood, weapon) {  
      var o = new Object();
      o.name = name;
      o.blood = blood;
      o.weapon = weapon;
      o.attack = function () {  
        console.log(this.weapon+'攻击敌人');
      }
      return o;
    }

	var hero = createHero('刘备',100,'剑');
3、构造函数

全为实例成员,创建的对象为hero,但多个对象会存储多个同样的attack方法,占用代码冗余

function Hero(name, blood, weapon) { 
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
      this.attack = function () {  
        console.log(this.weapon+'攻击敌人');
      }
    }

    var hero =new  Hero('刘备',100,'剑');

改1:

解决了存储问题,但污染了全局作用域

function Hero(name, blood, weapon) {
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
      this.attack = attack;
    }

    function attack() {
      console.log(this.weapon + '攻击敌人');
    }

    var hero = new Hero('刘备', 100, '剑');
4、使用原型

解决了存储问题,同样不污染全局作用域

function Hero(name, blood, weapon) {
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
    }
    Hero.prototype.attack = function () {  
      console.log(this.weapon + '攻击敌人');
    }
  
    var hero = new Hero('刘备', 100, '剑');

简单原型

在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象。原型对象,顾名思义,它就是一个普通对象。

img
在这里插入图片描述

原型链

在这里插入图片描述

继承

原型继承:

无法设置构造函数的参数

    function Person() {
      this.name = 'zs';
      this.age = '18';
      this.sex = '男'
    }

    function Student() {
      this.score = '99';
    }
    Student.prototype = new Person();
    Student.prototype.constructor = Student;

    function Teacher() {
      this.salary = 3000;
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JBpBys6Y-1570539608298)(C:\Users\10592\AppData\Roaming\Typora\typora-user-images\1564197765564.png)]

借用构造函数

方法怎么继承呢?

// 借用构造函数
    function Person(name, age, sex) {  
      this.name = name;
      this.age = age;
      this.sex = sex;
    }

    function Student(name, age, sex,score) { 
      Person.call(this,name,age,sex);
      this.score = score;
    }
组合继承:借用构造函数+原型继承
function Person(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    Person.prototype.sayHi = function () {
      console.log('大家好!我是' + this.name);
    }

    function Student(name, age, sex, score) {
      Person.call(this, name, age, sex);
      this.score = score;
    }
    // 让字类型继承父类型的方法
    Student.prototype = new Person();
    Student.prototype.constructor = Student;
    Student.prototype.exam = function () {
      console.log('考试');
    }

    function Student(name, age, sex, score) {
      Person.call(this, name, age, sex);
      this.score = score;
    }
    Teacher.prototype = new Teacher();
    Teacher.prototype.constructor = Teacher;
    Teacher.prototype.gaijuan = function () {
      console.log('改卷');
    }

函数

函数的定义方式
函数声明与函数表达式
// 函数的声明
    fn();
    function fn() {  
      console.log('test');
    } 
//报错
// 函数表达式
    fn1();
    var fn1= function () {  
      console.log('test');
    }

区别:

  • 上面代码由于声明提前可看成:所以fn1()处报错
//声明提前
function fn() {  
      console.log('test');
 } 
 var fn1;
 
 fn();
 fn1();
 fn1= function () {  
      console.log('test');
 }
  • 在老版本的IE中 if 语句中的函数声明会提前 但函数表达式并不会发生这样的情况
// 浏览器问题
    // 根据条件声明函数
    // 在现代浏览器 不会提升if语句中的函数声明
    // 在老版本的IE中  if语句中的函数声明会提前
    if(true){
      function fn() {
        console.log('test--true');
      }
    }else{
      function fn() {
        console.log('test--false');
      }
    }

运行结果:

现代浏览器:test--ture

IE8:test--false

new Function

不推荐使用,因为执行速度慢(先解析字符串,在执行)

认识到函数也是对象

var fn = new Function('var name = "张山";console.log(name);')
    fn();
    console.dir(fn);
this

函数内部的this,是由函数调用的时候来确定其指向

普通函数调用

this指向window

// 1、普通函数
    function fn() {}
    fn();
   //相当于window.fn();
方法调用

this指向obj,指向调用该方法的对象

// 2、方法调用 this指向obj
    var obj={
      fn:function () {}
    }
    obj.fn();
构造函数调用

构造函数内部的this指向由构造函数创建的对象,this指向hero实例

// 3、构造函数调用  构造函数内部的this指向由构造函数创建的对象
function Hero(name, blood, weapon) {
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
    }
    Hero.prototype.attack = function () {  
      console.log(this.weapon + '攻击敌人');
    }
  
    var hero = new Hero('刘备', 100, '剑');
    hero.attack();
作为事件处理函数调用

作为事件处理函数调用 this指向触发该对象的对象,this指向btn

// 4、作为事件处理函数调用  指向触发该对象的对象
    btn.onclick=function(){
      console.log(this);
    }
作为定时器的参数

作为定时器的参数 this指向window

// 5、作为定时器的参数  this指向window
    setInterval(function() {
      console.log(this);
    }, 1000);
函数中的方法
call的应用
  • 使伪数组使用数组的方法
  • 使arr数组可以使用ObjecttoString 而不是自己重写的toString 可以找到数组对象的类型
// 伪数组
    var obj={
      0: 100,
      1: 60,
      2: 60,
      length: 3
    };

    Array.prototype.push.call(obj,30);
    Array.prototype.splice.call(obj,0,2);
    console.dir(obj);

    var arr = [5,9];
    console.log(arr.toString());
    console.log(Object.prototype.toString.call(arr));

最后三句输出:

  • 5 ,9
  • object Array
apply的应用

可以把数组中的每一项展开

// apply
    var arrp = [6,10,5,9,3];
    
    console.log(Math.max.apply(Math,arr)); //输出:10
    console.log(null,arr); //输出:6 10 5 9 3
bind的应用
  • 改变定时器的this指向

  • 改变事件处理函数中的this指向

    改变定时器的this指向,让this为function中的this,而function的又是obj调用,所以this指向obj

 var obj={
      name:'zs',
      fun:function () {  
        setInterval(function() {
          console.log(this.name);  
        }.bind(this), interval);  //这里的this是function的this,
      }
    }
    obj.fn();//输出zs

    btn.onclick = function () {  

    }.bind(obj);
其他方法与arguments的应用

当函数参数不固定的时候,在函数内部可以通过arguments获取

//其他方法
    function fn(x,y) {  
      // 函数内部有一个私有变量 arguments(它不是fn.arguments)但跟fn.arguments作用一样(伪数组,获取到的是函数的实参)
      console.log(arguments);
      console.log(fn.arguments);  //伪数组,获取到的是函数的实参,可以根据arguments获取实参
      console.log(fn.caller);     //函数的调用者,全局调用时为null
      console.log(fn.name);       //函数的名称,字符串类型
      console.log(fn.length);     //函数的形参个数
    }
    function test() {  
      fn(1,3,6);
    }

    test();
	//当函数参数不固定的时候,在函数内部可以通过arguments获取
    function max() {  
      var max = arguments[0];
      for(var i=0;i<arguments.length;i++){
        if(max<arguments[i]){
          max=arguments[i];
        }
      }
      return max;
    }

    console.log(max(1,2,8,9,1,7,3));
高阶函数
函数作为参数与sort的内部实现
// 1、函数作为参数
    function eat(fn) {  
      setTimeout(function() {
        // 吃晚饭
        console.log('吃晚饭');
        // 吃晚饭后的事情
        fn();
      }, 2000);
    }
    eat(function() {  
      console.log('去唱歌');
    });

    var arr=[30,55,14,61];
    arr.sort(function(a, b) {
      return a - b;
    })
    console.log(arr);
    
    // arr.mySort
    Array.prototype.mySort = function (fn) {  
      for(var i=0;i<this.length-1;i++){
        var isSort = true;
        for(var j=0;j<this.length-i-1;j++){
          if(fn(this[j],this[j+1])>0){
            isSort =false;
            var temp = this[j];
            this[j] = this[j+1];
            this[j+1] = temp;
          }
        }
        if(isSort){
          break;
        }
      }
    }

    var arr=[30,55,14,61];
    arr.mySort(function(a,b) {
      return a-b;
    });
    console.log(arr);
函数作为返回值(可能发生闭包)
  • 写一个函数,第一次调用生成1-10随机数,以后的每一次调用都返回第一次的随机值
  • 求两个数的和n是变化的
    100+n
    1000+n
    10000+n
    第一个参数也会变化但却是规定的
  • 两个案例都发生了闭包
//写一个函数,调用生成1-10随机整数数
    function getRandom() {  
      return parseInt(Math.random()*10)+1;
    }
    console.log(getRandom());
    // 写一个函数,第一次调用生成1-10随机数,以后的每一次调用都返回第一次的随机值
    function getOnceRandom() {  
      var random=parseInt(Math.random()*10)+1;
      return function() {  
        return random;
      }
    }
    var fn = getOnceRandom();
    console.log(fn());
    console.log(fn());
    console.log(fn());
    // 求两个数的和
    // 100+n
    // 1000+n
    // 10000+n
    // 第一个参数也会变化但却是规定的
    function getFun(m) {
      return function(n) {  
        return m+n;
      }
    }

    var fn100=getFun(100);
    var fn1000=getFun(1000);

    console.log(fn100(15)); //115
    console.log(fn1000(15)); //1015

闭包

在一个作用域中可以访问另一个作用域的变量或者函数

上面两个案例随机数和求和都发生了闭包

//未发生闭包
    function fn1() {  
      var n=10;
      return n;
    }
    fn1();
    // 闭包
    // 特点:延展了函数作用域的范围
    function fun() {  
      var n=10;
      return function() {  
        return n;
      }
    }
    var f=fun();
    console.log(f());
案例1:

输出点击元素的索引

方式一更好,因为外部的自调用函数的作用域并没有释放性能变低,所以闭包不一定更好

// 案例1:输出点击元素的索引
    // 方式1:
    var heroes = document.getElementById('heroes');
    var list = heroes.children;
    for (var i = 0; i < list.length; i++) {
      var li = last[i];
      li.index = i;
      li.onclick = function () {
        console.log(this.index);
      }
    }
    // 方式2:使用闭包(第一种更好,因为外部的自调用函数的作用域并没有释放,所以闭包不一定更好)
    var heroes = document.getElementById('heroes');
    var list = heroes.children;
    for (var i = 0; i < list.length; i++) {
      var li = last[i];
      (function (i) {
        li.onclick = function () {
          console.log(i);
        }
      })(i);
    }
setTimeout的原理

在这里插入图片描述

console.log('start');
    setTimeout(function() {
      console.log('timeout');
    }, 0);

    console.log('over');

输出:start over timeout

案例2:

点击按钮改变字体大小

// 案例2:
    var btn01 = document.getElementById('btn01');
    var btn02 = document.getElementById('btn02');
    var btn03 = document.getElementById('btn03');

    // btn01.onclick = function () {
    //   document.body.style.fontSize = '12px';
    // }
    // btn02.onclick = function () {
    //   document.body.style.fontSize = '14px';
    // }
    // btn03.onclick = function () {
    //   document.body.style.fontSize = '16px';
    // }

    function makeFun(size) {  
      return function () {  
        document.body.style.fontSize = size +'px';
      }
    }

    btn01.onclick = makeFun(12);
    btn02.onclick = makeFun(14);
    btn03.onclick = makeFun(16);
思考1:

没有发生闭包

输出:The Window

var name = "The Window";
    var object={
      name:'My Obejct',
      getNameFun:function () {  
        return function () {  
          return this.name;
        }
      }
    }
    console.log(object.getNameFun()()); //The Window
思考2:

发生了闭包

输出:My Obejct

// 思考2:
    var name = "The Window";
    var object = {
      name: 'My Obejct',
      getNameFun: function () {
        var that =this;
        return function () {
          return that.name;
        }
      }
    }
    console.log(object.getNameFun()()); //My Obejct

递归:

一般都要写一个结束条件,不然会堆栈溢出

案列1:

递归实现1+2+3+4+…+n

// 递归实现1+2+3+4+...+n
    function getSum() {
      if (n===1) {
        return 1;
      }
      return n+getSum(n-1);
    }
案例2:

获取斐波那契数列的第n个数
数列:1、1、2、3、5、8、13、21、34

// 斐波那契数列的第n个数
    // 数列:1、1、2、3、5、8、13、21、34
    
    function getFB(n) {  
      if(n===1||n===2){
        return 1;
      }
      return getFB(n-1)+getFB(n-2);
    }
浅拷贝:

改变拷贝前的对象,发现普通属性没有随着拷贝前的对象的改变而变化,但是当属性也是一个对象时,会发生改变

 // 对象的浅拷贝
    var obj1 = {
      name: 'zs',
      age: 18,
      sex: '男',
      dog: {
        name: '金毛',
        age: 5,
        yellow: '黄色'
      }
    }
    var obj2 = {}
    // 浅拷贝封装
    function copyObject(o1, o2) {
      for (var key in o1) {
        o2[key] = o1[key];
      }
    }
    copyObject(obj1, obj2);

    obj1.name = 'xxxx';
    obj1.dog.name = '大黄';
    console.dir(obj2);

在这里插入图片描述

深拷贝:

不管属性是什么类型,改变拷贝前的对象都不会影响拷贝后的对象

// 深拷贝:
    var obj1 = {
      name: 'zs',
      age: 18,
      sex: '男',
      dog: {
        name: '金毛',
        age: 5,
        yellow: '黄色'
      }
    }
    var obj2 = {}
    // 深拷贝:
    function deepCopy(o1,o2) {  
      for (var key in o1) {
        var item = o1[key];
        if(item instanceof Object){
          o2[key]={};
          deepCopy(item,o2[key]);
        }else if (item instanceof Array){
          o2[key]=[];
          deepCopy(item,o2[key]);
        }else{
          o2[key] = o1[key];
        }
      }
    }
    deepCopy(obj1,obj2);

    obj1.name = 'xxxx';
    obj1.dog.name = '大黄';
    console.dir(obj2);
    console.log(obj1);
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值