JavaScript高级

原型

每一个构造函数都有一个属性 --> 原型 / 原型对象

function Student(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}

// 为构造函数添加sayHi方法
Student.prototype.sayHi = function () {
    console.log("大家好,我是" + this.name);
}

varr stu1 = new Student('a', 19, '男');
var stu2 = new Student('b', 20, '女');

console.log(stu1.sayHi === stu2.sayHi); // true
  • 当使用对象的属性或者方法的时候,先去找对象本身的属性/方法,如果对象没有改属性和方法,此时去调用原型中的属性/方法。

  • 如果对象本身没有该属性/方法,原型中也没有该属性和方法,此时会报错

  • 对象的__ proto __等于构造函数的Student.prototype

  • __ proto __ 属性是非标准的属性,在真实开发环境中不能去使用

  • 在原型对象中有一个属性 constructor 指向构造函数

  • constructor 作用记录了创建该对象的构造函数,记录了创建改对象的构造函数。

构造函数、原型对象、实例/对象 之间的关系

在这里插入图片描述

构造函数原型对象 prototype 指向谁?

原型链

在这里插入图片描述

属性的查找

  • 读取属性

    现在对象本身查找属性,如果没有找到的话,回去原型链上查找

  • 设置属性

    如果对象本身没有这个属性,不会搜索原型链,而是直接在对象本身增加这个属性

注意点

一般情况下,对象的属性在构造函数中来设置,对象的方法在构造函数的原型对象中来设置

function Student(name, age) {
    this.name = name;
    this.age = age;
}
// 设置方法
Student.prototype.sayHi = function () {
    console.log('sayHi');
}

如果方法过多,那么上一种方法中增加方法会不太方便,推荐下一种

Student.prototype = {
    sayHi:function () {
        //代码
    }.
    eat: function(){
        //代码
    }
}

上一种增加构造函数方法虽然简单,但是他的constructor指向就会指向Object,如果想让他指向没有问题,用下面的代码中来解决

Student.prototype = {
    constructor:Student, 
    sayHi:function () {
        //代码
    }.
    eat: function(){
        //代码
    }
}

所以当我们改变构造函数的prototype的时候,需要重新设置constructor属性

还有一点就是当使用给prototype赋值为一个对象来实现增加方法的时候,应该先去设置原型属性,在创建对象,才可以访问访问原型对象中的成员

原型对象的应用

扩展内置对象

如果想让内置对象Array想求出所有偶数的和,那么就需要扩展原型对象的方法。

var array = [5, 4, 1, 8];
Array.prototype.getSum = function () {
      // 求数组中所有偶数的和
      var sum = 0;
      for (var i = 0; i < this.length; i++) {
        if (this[i] % 2 === 0) {
          sum += this[i];
        }
      }
      return sum;
    }
console.log(array.getSum());

那么是否可以使用以下的方式进行扩展呢?

  Array.prototype = {
       getSum: function () {
          // 求数组中所有偶数的和
         var sum = 0;
         for (var i = 0; i < this.length; i++) {
           if (this[i] % 2 === 0) {
           sum += this[i];
           }
         }
        return sum;
      }
   }

注意,这种方法时不可取的,因为数组或者String 中的prototype是不可以修改的,即使可以修改,Array原先的prototype已经给我提供了许多方法,这样扩展的话,原先的方法就会丢失

bind方法

改变this的指向

var a = 123;
function fn () {
    console.log(this.a); // 123
}
// 利用bind改变指向
var obj = {
    a:'abc'
}
var fn1 = fn.bind(obj);
fn1(); // 打印abc

自调用函数的参数

  • 自调用函数传入window的目的,是让变量名可以被压缩

  • 传入undefined目的是,在老版本的浏览器中,undefined可以被重新复制

    ;(function (window, undefined) {
        // 代码
    })(window, undefined)
    

继承

面向对象三大特征:封装、继承、多态(抽象)

JavaScript 不支持多态,可以理解为抽象

对象间的继承(对象的拷贝)

var wjl = {
      name: '王健林',
      money: 10000000,
      cars: ['玛莎拉蒂', '特斯拉'],
      houses: ['别墅', '大别墅'],
      play: function () {
        console.log('打高尔夫');
      }
    }

var wsc = {
  name: '王思聪'
}

// 对象的拷贝
// 复制对象的成员给另一个对象
function extend(parent, child) {
  for (var key in parent) {
    // 不给wsc复制同名的属性
    if (child[key]) {
      continue;
    }
    child[key] = parent[key];
  }
}

extend(wjl, wsc);

console.dir(wsc);

原型继承(并不推荐使用)

继承:类型和类型之间的关系
学生类型 老师类型 -> Person类型
继承目的: 把子类型中共同的成员提取到父类型中,代码重用

原型继承:无法设置构造函数的参数

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

// 子类型
function Student() {
  this.score = 100;
}
function Teacher() {
  this.salary = 3000;
}

// 测试代码
// 继承person
Student.prototype = new Person();
// 改变学生对象的constructor的指向
Student.prototype.constructor = Student;

var s1 = new Student();
console.log(s1.constructor);
console.dir(s1);

call

回顾一下之前的bind(this指向, 形参, 形参)

bind() 改变函数的this,并且返回一个新的函数(不调用函数)

function fn(x, y) {
  console.log(this);
  console.log(x + y);
}

// window.fn(5, 6);

var o = {
  name: 'zs'
};

// bind()   改变函数的this,并且返回一个新的函数  (不调用函数)

var f = fn.bind(o, 1, 2);
f();

call(this指向, 形参, 形参)

call() 改变函数中的this,直接调用函数

fn.call(o, 2, 3);

借用构造函数

继承的另一种方式,缺点是无法继承方法

// 借用构造函数
// 父类型
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
  // this.sayHi
}
Person.prototype.sayHi = function () {
  console.log(this.name);
}

// 子类型
function Student(name, age, sex, score) {
  Person.call(this, name, age, sex);
  this.score = score;
}

var s1 = new Student('zs', 18, '男', 100);
console.dir(s1);

组合继承

组合继承:借用构造函数 + 原型继承

 // 父类型
    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('考试');
    }

    // var s1 = new Student('zs', 18, '男', 100);
    // console.dir(s1);


    // var p1 = new Person('ls', 18, '男');
    // console.dir(p1);


    function Teacher(name, age, sex, salary) {
      // 借用构造函数
      Person.call(this, name, age, sex);

      this.salary = salary;
    }

    // 通过原型让子类型继承父类型中的方法
    Teacher.prototype = new Person();
    Teacher.prototype.constructor = Teacher;

    var t1 = new Teacher('ww', 30, '男', 100000);
    console.dir(t1);

    t1.sayHi();

在这里插入图片描述

函数进阶

函数的声明方式

  1. // 1.函数声明
    function fn() {
        // 代码
    }
    fn();
    
  2. // 2.函数表达式
    fn(); // 在这里会报错,注意变量提升
    var fn = function () {
        // 代码
    }
    fn(); // 正常执行
    

现代浏览器,不会提升if语句中的声明,老版本浏览器会提升,可以利用函数表达式的方式进行定义

  1. //3. new Function()
    var fn = new Function('var name = "haha";console.log(name)');
    fn();
    console.dir(fn);
    // 执行速度慢,但是可以认识到函数也是一个对象
    

函数的调用方式

  1. 普通函数调用
    • this 指向 window
  2. 方法调用
    • this 指向 调用改方法的对象
  3. 作为构造函数调用
    • this 指向由该构造函数创建的对象
  4. 作为事件的处理函数
    • this 触发该事件的对象
  5. 作为定时器的参数
    • this 指向 window

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

改变函数中的this

  1. call()

    • 功能: 调用函数,改变函数中的this
    • 参数: 第一个参数,设置函数内部this的访问,其他参数为形参
    • 返回值:call()返回值就是函数的return的返回值

    应用:

     // getElementsByTagName()
    // 伪数组
    var obj = {
      0: 100,
      1: 10,
      2: 11,
      3: 20,
      length: 4
    };
    
    // 利用Array里面的push方法添加
    Array.prototype.push.call(obj, 'a', 'n');
    // 利用Array里面的splice方截取
    Array.prototype.splice.call(obj, 0, 3);
    
  2. bind()

    bind的应用

    var obj = {
      name: 'zs',
      fun: function() {
        setInterval(function() {
          console.log(this.name);
        }.bind(this), 1000);
      }
    }
    obj.fun();
    
    
    btn.onclick = function () {
      // 事件处理函数中的this  是触发该事件的对象
    
      // 通过bind 改变事件处理函数中this的指向
    
  3. apply()

    第一个参数为this的指向(一般不需要更改),第二个参数为一个数组

    apply的应用

    // 2 
        // fn.apply(,[])
    
        // Math.max(3, 5, 6);
    
        var arr = [5, 10, 1, 3, 6];
        // Math.max不能求数组中的最大值
        // console.log(Math.max(arr));
    
        console.log(Math.max.apply(Math, arr));
        
    
        // console.log(1, 2, 3);
        // console.log(arr);
    
        console.log.apply(console, arr);
    

函数中的属性

  1. arguments

    伪数组,获取到的时是函数的实参

  2. caller

    函数的调用者,在全局范围调用的时候caller是null

  3. length

    函数的形参的个数

  4. name

    函数的名称,字符串类型

  5. 函数内部有个私有

高阶函数

以下两种方式成为高阶函数

  1. 函数作为参数

  2. 函数作为返回值的时候

    应用

    //  函数作为返回值的时候
        
    // 写一个函数,生成1-10之间的随机整数
     function getRandom() {
       return parseInt(Math.random() * 10) + 1;
     }
     console.log(getRandom());
    
     //写一个函数,生成1-10之间的随机整数
    // 第一次调用生成随机数,以后每次调用都返回第一次的随机值
    
     function getRandom() {
       var random = parseInt(Math.random() * 10) + 1;
       return function () {
         return random;
       }
     }
    
     var fn = getRandom();
    
     console.log(fn());
     console.log(fn());
     console.log(fn());
    

闭包

闭包是使用被作用域封闭的遍历,函数,闭包等执行的一个函数的作用域。通常我们用和其相应的函数来指代这些作用域。(可以访问独立数据的函数)

function fn() {
  var n = 10;
  return function () {
    return n;
  }
}
var f = fn();
console.log(f());

特点

延展了函数的作用域范围

闭包案例

​ 点击li输出他对应的索引

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <ul>
        <li>001</li>
        <li>002</li>
        <li>003</li>
        <li>004</li>
        <li>005</li>
    </ul>

    <script>
        var ul = document.getElementsByTagName('ul')[0];

        for (var i = 0, len = ul.children.length; i < len; i++) {
            var li = ul.children[i];

            (function (i) {
                li.onclick = function () {
                    console.log(i);
                }
            })(i)
        }
    </script>
</body>

</html>

定时器的执行过程

​ js开始执行的时候,会把所有的代码先放到执行栈中,当遇到setTimeout的时候,会把里面的匿名函数放到任务队列中,当执行栈中所有的代码处理完成之后才会处理任务队列中的代码。

console.log('start');

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

console.log('over');

// 输出结果
// start over timeout

在这里插入图片描述

对象的拷贝

浅拷贝 - 单层复制

只可以复制原对象中的基本数据类型,而对象类型无法复制。

// 对象的拷贝
var obj1 = {
    name : 'zs',
    sex : '男',
    dog : {
       var name = '大黄';
    }
}
var obj2 = {}

for (var key in obj1) {
    obj2[key] = obj1[key];
}
// obj1内容中的基本类型修改不会影响obj2,但是obj1的dog对象修改会影响obj2
console.log(obj2);

深拷贝 - 多层复制

// 对象的拷贝
var obj1 = {
    name : 'zs',
    sex : '男',
    dog : {
       var name = '大黄';
    }
}
var obj2 = {}
// 深拷贝(o1的成员拷贝给o2)
function deepCopy (o1, 02) {
    for (var key in o1) {
        // 获取key属性对应的值
        var item = o1[key];
        
        if (item instanceof Object) {
            deepCopy(item, o2[key]);
        } else if () {
        	deepCopy(item, o2[key]);
        } else {
			o2[key] = o1[key];
        }
    }
}
console.log(obj2);

遍历DOM树

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>遍历DOM树</title>
</head>
<body>
  <h1>遍历 DOM 树</h1>
  <p style="color: green;">Tip: 可以在遍历的回调函数中任意定制需求</p>
  <div>
    <ul id="list">
      <li>123</li>
      <li>456</li>
      <li>789</li>
    </ul>
    <div>
      <div>
        <span>haha</span>
      </div>
    </div>
  </div>
  <div id="demo_node">
    <ul>
      <li>123</li>
    </ul>
    <p>hello</p>
    <h2>world</h2>
    <div>
      <p>dsa</p>
      <h3>
        <span>dsads</span>
      </h3>
    </div>
  </div>
  <script>
    // 遍历指定元素下所有的子元素
    function loadTree(parent, callback) {
      for (var i = 0; i < parent.children.length; i++) {
        // 遍历第一级子元素
        var child = parent.children[i];
        // console.log(child);
        if (callback) {
          // 处理找到的子元素
          callback(child);
        }

        // 递归调用
        loadTree(child);
      }
    }

    var ul = document.getElementById('list');
    loadTree(ul, function (element) {
      element.onclick = function () {
        console.log(this.innerText);
      }
    });
  </script>
</body>
</html>

正则表达式

元字符

元字符说明
\d匹配数字
\D匹配任意非数字的字符
\w匹配字母或数字或下划线
\W匹配任意不是字母,数字,下划线
\s匹配任意的空白符
\S匹配任意不是空白符的字符
.匹配除换行符以外的任意单个字符
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)

限定符

限定符说明
*重复零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次

##其他

[] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思
[^] 匹配除中括号以内的内容
\ 转义符
| 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱
() 从两个直接量中选择一个,分组
eg:gr(a|e)y匹配gray和grey
[\u4e00-\u9fa5] 匹配汉字

声明方式

  1. var regularExpression = new RegExp(模式pattern, flag);

    flag

    • i 忽略大小写
    • g 全局匹配
  2. var regularExpression = /^\d{5,12}$/;
    

案例

验证手机号:

^\d{11}$

验证邮编:

^\d{6}$

验证日期 2012-5-01

^\d{4}-\d{1,2}-\d{1,2}$

验证邮箱 xxx@itcast.cn

^\w+@\w+\.\w+$

验证IP地址 192.168.1.10

^\d{1,3}\(.\d{1,3}){3}$

参数

标志说明
i忽略大小写
g全局匹配
gi全局匹配+忽略大小写

分割

 // 1. 提取日期中的年部分  2015-5-10
// var dateStr = '2015-1-5';
// console.log(dateStr.split('-'));

// var dateStr = '2015/1-5';
// console.log(dateStr.split(/[/-]/));


// 2. 提取邮件中的每一部分
var str = 'xxxx@itcast.com';
console.log(str.split(/[@\.]/)); // 以@或者.进行分割

替换

// 1. 提取工资
var str = "张三:1000,李四:5000,王五:8000。";
var array = str.match(/\d+/g);
console.log(array);

// 2. 提取email地址
var str = "123123@xx.com,fangfang@valuedopinions.cn 286669312@qq.com 2、emailenglish@emailenglish.englishtown.com 286669312@qq.com...";
var array = str.match(/\w+@\w+\.\w+(\.\w+)?/g);
console.log(array);

// 3. 分组提取  
// 3. 提取日期中的年部分  2015-5-10
var dateStr = '2016-1-5';
// 正则表达式中的()作为分组来使用,获取分组匹配到的结果用Regex.$1 $2 $3....来获取
var reg = /(\d{4})-\d{1,2}-\d{1,2}/;
if (reg.test(dateStr)) {
  console.log(RegExp.$1);
}

// 4. 提取邮件中的每一部分
var reg = /(\w+)@(\w+)\.(\w+)(\.\w+)?/;
var str = "123123@xx.com";
if (reg.test(str)) {
  console.log(RegExp.$1);
  console.log(RegExp.$2);
  console.log(RegExp.$3);
}

替换

// 1. 替换所有空白
var str = "   123AD  asadf   asadfasf  adf ";
str = str.replace(/\s/g,"xx");
console.log(str);

// 2. 替换所有,|,
var str = "abc,efg,123,abc,123,a";
str = str.replace(/,|,/g, ".");
console.log(str);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值