前端视频学习(六、JavaScript高级)

本文深入探讨JavaScript的高级特性,包括基本类型与复杂类型的区分,重点讲解对象、原型和原型链。介绍了创建对象的多种方式,特别是原型对象在构造函数与实例对象间的角色,以及如何利用原型实现数据共享和继承。同时,文章讨论了函数的高级特性,如this的指向、apply、call和bind方法,以及严格模式、作用域链和闭包等核心概念。最后,简要触及正则表达式和浅拷贝、深拷贝等知识点。
摘要由CSDN通过智能技术生成

目录

在这里插入图片描述

知乎 - JavaScript 能做什么,该做什么?

JavaScript 标准参考教程 - JavaScript 语言的历史

0. 复习

基本类型(值类型)
  • Undefined
  • Null
  • Boolean
  • Number
  • String

JS中String是值类型,和C#、Java不同!

复杂类型(引用类型)
  • Object
  • Array
  • Date
  • RegExp
  • Function
  • 基本包装类型
    • Boolean
    • Number
    • String
  • 单体内置对象
    • Global
    • Math
类型检测
  • typeof
  • instanceof
  • Object.prototype.toString.call()

JavaScript 执行过程

JavaScript 运行分为两个阶段:

  • 预解析
    • 全局预解析(所有变量和函数声明都会提前;同名的函数和变量函数的优先级高)
    • 函数内部预解析(所有的变量、函数和形参都会参与预解析)
      • 函数
      • 形参
      • 普通变量
  • 执行

先预解析全局作用域,然后执行全局作用域中的代码,
在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内代码。

1. 对象

  • JS是一个基于对象的语言
  • ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数
  • 每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型
  • JS中的面向对象特性继承是通过原型来实现的。

1.1 创建对象的方法

  1. 字面量
  2. 使用系统的构造函数new Object()
  3. 工厂方法
  4. 自定义构造方法
// 构造函数创建对象
function Person (name, age) {
   
  this.name = name
  this.age = age
  this.sayName = function () {
   
    console.log(this.name)
  }
}

var p1 = new Person('Jack', 18)
p1.sayName() // => Jack

var p2 = new Person('Mike', 23)
p2.sayName() // => Mike

1.2 构造函数创建对象的过程

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
  3. 执行构造函数中的代码
  4. 返回新对象
// 伪代码
function Person (name, age) {
   
  // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象
  // var instance = {}
  // 然后让内部的 this 指向 instance 对象
  // this = instance
  // 接下来所有针对 this 的操作实际上操作的就是 instance

  this.name = name
  this.age = age
  this.sayName = function () {
   
    console.log(this.name)
  }

  // 在函数的结尾处会将 this 返回,也就是 instance

2. ★原型prototype

Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承

  • 原型的作用:
    1. 共享数据,节省内存空间
    2. 实现继承

2.1 构造函数和实例对象之间的关系——constructor

function Person(name, age){
   
    this.name = name;
    this.age = age;
}
var p1 =new Person("Gedy", 18);
console.dir(p1);
console.dir(Person);

console.log(p1.__proto__.constructor === Person);  // true
console.log(p1.constructor === Person);   // true
console.log(p1.constructor === Person.prototype.constructor);   // true
console.log(Person.prototype.constructor === Person);   // true

在这里插入图片描述

关系

  • 对象的__proto__.constructor属性,指向的就是构造函数本身
  • 构造函数的prototype.constructor属性,指向的也是自己本身

但是判断对象属于什么类型,还是推荐使用instanceof

2.2 构造函数方法属性的问题——匿名方法内存浪费

    function Person(name, age){
   
        this.name = name;
        this.age = age;
        this.eat = function(){
   
            console.log("哈哈哈");
        }
    }

上面的构造函数中,每创建一个对象,就会创建一个匿名方法,大量的对象会导致内存浪费。

2.3 ★构造方法、实例对象、原型对象之间的关系

  • 构造函数可以实例化对象
  • 构造函数中有一个属性叫prototype,是构造函数的原型对象
  • 构造函数的原型对象(prototype)中有一个constructor构造器,这个构造器指向的就是自己所在的原型对象所在的构造函数
  • 实例对象的原型对象(__proto__)指向的是该构造函数的原型对象
  • 构造函数的原型对象(prototype)中的方法是可以被实例对象直接访问的

在这里插入图片描述

2.4 ★使用prototype添加实例共享方法/属性

  • 实例对象中有个属性,__proto__,也是对象,叫原型,不是标准的属性,浏览器使用的

  • 构造函数中有一个属性,prototype,也是对象,叫原型,是标准属性,程序员使用

    • 原型:__proto__或者是prototype,都是原型对象,

案例1:

function Person(name,age) {
   
  this.name=name;
  this.age=age;
}
//通过原型来添加方法,解决数据共享,节省内存空间
Person.prototype.eat=function () {
   
  console.log("吃凉菜");
};

var p1=new Person("小明",20);
var p2=new Person("小红",30);
console.log(p1.eat==p2.eat);//true

案例2:

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];
    }
  };
};

简单的原型使用语法

与其一个一个设置属性,不如将原型对象设置成一整个对象,但是要注意的是,一定要手动加上constructor属性,否则这个属性就丢失了。

Student.prototype = {
   
  //手动修改构造器的指向
  constructor:Student, // 这个一定要手动加上!!
  height: "188",
  weight: "55kg",
  study: function () {
   
    console.log("学习好开心啊");
  },
  eat: function () {
   
    console.log("我要吃好吃的");
  }
};

2.5 ★属性成员的搜索原则:原型链

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性

  1. 搜索首先从对象实例本身开始
  2. 如果在实例中找到了具有给定名字的属性,则返回该属性的值
  3. 如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性
  4. 如果在原型对象中找到了这个属性,则返回该属性的值

搜索顺序: 实例对象 --> 原型

案例:

function Person(age,sex) {
   
      this.age=age;//年龄
      this.sex=sex;
      this.eat=function () {
   
        console.log("构造函数中的吃");
      };
    }
Person.prototype.sex="女";
Person.prototype.eat=function () {
   
  console.log("原型对象中的吃");
};

var per=new Person(20,"男");
console.log(per.sex);//男,因为先搜索实例对象
per.eat(); // 构造函数中的吃, 因为先搜索实例对象

2.6 原型中的函数互相调用——this.方法名

  • 在给原型注册方法的时候,方法中的this指代的是实例对象
  • 原型中的一个方法访问另一个方法,也是使用this.方法名, this代表实例对象。
function Animal(name,age) {
   
      this.name=name;
      this.age=age;
    }
    //原型中添加方法
    Animal.prototype.eat=function () {
   
      console.log("动物吃东西");
      this.play();
    };
    Animal.prototype.play=function () {
   
      console.log("玩球");
      this.sleep();
    };
    Animal.prototype.sleep=function () {
   
      console.log("睡觉了");
    };

    var dog=new Animal("小苏",20);
    dog.eat();

2.7 为内置对象添加方法

直接给Array, String, Dateprototype添加方法就行了。

    String.prototype.myReverse=function () {
   
      for(var i=this.length-1;i>=0;i--){
   
        console.log(this[i]);
      }
    };
    var str="abcdefg";
    str.myReverse();


    //为Array内置对象的原型对象中添加方法
    Array.prototype.mySort=function () {
   
      for(var i=0;i<this.length-1;i++){
   
          for(var j=0;j<this.length-1-i;j++){
   
              if(this[j]<this[j+1]){
   
                  var temp=this[j];
                this[j]=this[j+1];
                this[j+1]=temp;
              }//end if
          }// end for
      }//end for
    };

2.8 ★把局部变量变为全局变量——作为window的属性

(function (win) {
   
	var num=10;//局部变量
//      //js是一门动态类型的语言,对象没有属性,点了就有了
	win.num=num;
})(window);

console.log(num);

2.9 封装私有函数—— 参考沙箱

3. 贪吃蛇案例

  • 没有做的功能

    • 何时游戏胜利
    • 撞身体会输
    • 食物不会出现在蛇身体上
  • 注意点:

    • 蛇的运动方法一次只执行一步,定时器的运动交给Game类来做,因为他要在每次运动之后判断游戏是否结束
    • 按键事件是给document来注册的
    • 在定时器以及 给document注册按键时,都需要用bind来改变函数的this指向
    • 蛇的身体边长一个,只需要增加一个位置,并且设置坐标和前一个一模一样即可,在下次运动的时,它的坐标就会保持不变,这样就能拉开距离。

  • 地图的方法:
    • init: 设置地图的尺寸和格子数,绘制地图
  • 食物的方法:
    • init:清除地图上原有的食物,然后随机生成坐标,创建一个食物元素,绘制在地图上
    • clear:在地图上消除食物
  • 蛇的方法:
    • init:先清除地图上的蛇,然后根据蛇身体的坐标和颜色,绘制在地图上
    • run:根据蛇的 方向,计算下一步的坐标
    • grow:让蛇边长一个身体
    • clear:在地图上清除蛇
  • 游戏对象:
    • start:初始化所有对象,调用其他方法开始游戏
    • runGame:开启定时器来玩蛇,判断是否游戏结束,以及是否吃到食物
    • bindkey:注册按键事件来改变蛇的方向
    • endGame:停止游戏定时器,弹出提示
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>

    </style>
</head>
<body>

<script src="jquery-1.12.4.js"></script>
<script>
    $(function () {
    

        // 公共方法
        (function (window) {
    
            Util = {
    
                getRandomValue: function (min, max) {
    
                    return parseInt(Math.random() * (max - min) + min)
                },

            };
            window.Util = Util;
        }(window));

        // 地图类
        (function (window) {
    
            function Map(widthPixel, heightPixel, widthGrid, heightGrid) {
    
                this.widthPixel = widthPixel || 400;
                this.heightPixel = heightPixel || 400;
                // 传递一共横竖几个格子
                this.widthGrid = widthGrid || 20;
                this.heightGrid = heightGrid || 20;
                // 地图元素
                this.element = null;
                // 地图内单个小块的尺寸
                this.gridWidthSize = null;
                this.gridHeightSize = null;

            }

            // 初始化方法
            Map.prototype.init = function () {
    
                // 创建div
                var element = document.createElement("div");
                // 设置样式和尺寸
                element.className = "map";
                element.style.width = this.widthPixel + "px";
                element.style.height = this.heightPixel + "px";
                element.style.backgroundColor = "grey";
                element.style.position = "relative";
                //     加到body中
                document.body.appendChild(element);
                // 计算单个小方块的尺寸
                this.gridWidthSize = this<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值