前端学习从入门到高级全程记录之29(JavaScript高级1)

学习目标

本期开始我们学习javascript高级知识。
学习目标:

  • 理解面向对象开发思想
  • 掌握 JavaScript 面向对象开发相关模式
  • 掌握在 JavaScript 中使用正则表达式

1.复习

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>

    /*
    *
    * JavaScript简称:JS
    * JS分三个部分:
    * 1 ECMAScript标准---基础的语法
    * 2 DOM  Document Object Model 文档对象模型
    * 3 BOM Browser Object Model 浏览器对象模型
    * 什么是JS?
    * 是一门脚本语言
    * 是一门解释性语言
    * 是一门弱类型语言
    * 是一门基于对象的语言
    * 是一门动态类型的语言
    *
    * 动态页面:页面由html+css+Js
    * 向服务器发送请求,服务器那边没有页面,是动态的生成,返回给客户端
    *
    * js最初的目的:解决用户和服务器之间的交互问题
    * js做特效,游戏,移动端,服务端
    *
    *
    *
    *
    * 编程思想:
    * 面向过程:所有的事情都是亲力亲为,注重的是过程
    * 面向对象:提出需求,找对象,对象解决,注重的是结果
    * js不是一门面向对象的语言,是基于对象的语言,js来模拟面向对象
    * 面向对象的特性:封装,继承,多态,(抽象性)
    * 封装:就是包装,把一些重用的内容进行包装,在需要的时候,直接使用
    * 把一个值,存放在一个变量中,把一些重用的代码放在函数中,把好多相同功能的函数放在一个对象中,把好多功能的对象,放在一个文件中,把一些相同的内容放在一个对象中
    *
    * 继承:类与类之间的关系,js中没有类的概念,js中有构造函数的概念,是可以有继承的,是基于原型
    *
    * 多态:同一个行为,针对不同的对象,产生了不同的效果
    * */
  </script>
</head>
<body>
</body>
</html>

基本概念

本小节快速过即可,主要是对学过的内容做知识点梳理。

  • 语法
    • 区分大小写
    • 标识符
    • 注释
    • 严格模式
    • 语句
  • 关键字和保留字
  • 变量
  • 数据类型
    • typeof 操作符
    • Undefined
    • Null
    • Boolean
    • Number
    • String
    • Object
  • 操作符
  • 流程控制语句
  • 函数

JavaScript 中的数据类型

JavaScript 有 5 种简单数据类型:Undefined、Null、Boolean、Number、String 和 1 种复杂数据类型 Object 。

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

值类型和引用类型在内存中的存储方式

  • 值类型按值存储
  • 引用类型按引用存储

值类型复制和引用类型复制

  • 值类型按值复制
  • 引用类型按引用复制

值类型和引用类型参数传递

  • 值类型按值传递
  • 引用类型按引用传递

值类型与引用类型的差别

  • 基本类型在内存中占据固定大小的空间,因此被保存在栈内存中
  • 从一个变量向另一个变量复制基本类型的值,复制的是值的副本
  • 引用类型的值是对象,保存在堆内存
  • 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针
  • 从一个变量向另一个变量复制引用类型的值的时候,复制是引用指针,因此两个变量最终都指向同一个对象

小结

  • 类型检测方式
  • 值类型和引用类型的存储方式
  • 值类型复制和引用类型复制
  • 方法参数中 值类型数据传递 和 引用类型数据传递

JavaScript 执行过程

JavaScript 运行分为两个阶段:

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

先预解析全局作用域,然后执行全局作用域中的代码,

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

JavaScript 面向对象编程

面向对象介绍

什么是对象
Everything is object (万物皆对象)

对象到底是什么,我们可以从两次层次来理解。
(1) 对象是单个事物的抽象。

一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。

(2) 对象是一个容器,封装了属性(property)和方法(method)。

属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。

在实际开发中,对象是一个抽象的概念,可以将其简单理解为:数据集或功能集。

ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。

严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值

提示:每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型。

什么是面向对象

面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。

面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。

它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。

因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。

面向对象与面向过程:

  • 面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊
  • 面向对象就是找一个对象,指挥得结果
  • 面向对象将执行者转变成指挥者
  • 面向对象不是面向过程的替代,而是面向过程的封装

面向对象的特性:

  • 封装性
  • 继承性
  • [多态性]

程序中面向对象的基本体现

在 JavaScript 中,所有数据类型都可以视为对象,当然也可以自定义对象。

自定义的对象数据类型就是面向对象中的类( Class )的概念。

我们以一个例子来说明面向过程和面向对象在程序流程上的不同之处。

假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个对象表示:

var std1 = { name: 'Michael', score: 98 }
var std2 = { name: 'Bob', score: 81 }

而处理学生成绩可以通过函数实现,比如打印学生的成绩:

function printScore (student) {
  console.log('姓名:' + student.name + '  ' + '成绩:' + student.score)
}

如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,

而是 Student 这种数据类型应该被视为一个对象,这个对象拥有 name 和 score 这两个属性(Property)。

如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个 printScore 消息,让对象自己把自己的数据打印出来。

抽象数据行为模板(Class):

function Student (name, score) {
  this.name = name
  this.score = score
}

Student.prototype.printScore = function () {
  console.log('姓名:' + this.name + '  ' + '成绩:' + this.score)
}

根据模板创建具体实例对象(Instance):

var std1 = new Student('Michael', 98)
var std2 = new Student('Bob', 81)

实例对象具有自己的具体行为(给对象发消息):

std1.printScore() // => 姓名:Michael  成绩:98
std2.printScore() // => 姓名:Bob  成绩 81

面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。

Class 是一种抽象概念,比如我们定义的 Class——Student ,是指学生这个概念,

而实例(Instance)则是一个个具体的 Student ,比如, Michael 和 Bob 是两个具体的 Student 。

所以,面向对象的设计思想是:

  • 抽象出 Class
  • 根据 Class 创建 Instance
  • 指挥 Instance 得结果

面向对象的抽象程度又比函数要高,因为一个 Class 既包含数据,又包含操作数据的方法。

2.三种创建对象的方式

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>
    //对象:特指的某个事物,具有属性和方法(一组无序的属性的集合)
    //特征---->属性
    //行为---->方法
    //小苏:------>姓名,性别,年龄,吃,睡觉,玩
    
    
    //创建对象三种方式:
    
    //1  字面量的方式
    //2  调用系统的构造函数
    //3  自定义构造函数方式
    
    //实例对象
//    var per1={
//      name:"卡卡西",
//      age:20,
//      sex:"男",
//      eat:function () {
//        console.log("吃臭豆腐");
//      },
//      readBook:function () {
//        console.log("亲热天堂");
//      }
//    };
//
//    //调用系统的构造函数
//    var per2=new Object();
//    per2.name="大蛇丸";
//    per2.age=30;
//    per2.sex="男";
//    per2.eat=function () {
//      console.log("吃榴莲");
//    };
//    per2.play=function () {
//      console.log("这个小蛇真好玩");
//    };
//
//    function Person() {
//
//    }
//    console.log(per2 instanceof Object);
//
//    var dog=new Object();


    //自定义构造函数
    function Person(name,age,sex) {
      this.name=name;
      this.age=age;
      this.sex=sex;
      this.play=function () {
        console.log("天天打游戏");
      };
    }
    var per=new Person("雏田",18,"女");
    console.log(per instanceof Person);


  </script>
</head>
<body>


</body>
</html>

简单方式的改进:工厂函数

通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。

我们可以写一个函数,解决代码重复问题:

function createPerson (name, age) {
  return {
    name: name,
    age: age,
    sayName: function () {
      console.log(this.name)
    }
  }
}

然后生成实例对象:

var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)

这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,

但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

工厂模式于构造函数的区别

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>
    
    function createObject(name,age) {
      var obj=new Object();
      obj.name=name;
      obj.age=age;
      obj.sayHi=function () {
        console.log("您好");
      };
      return obj;
    }
    function Person(name,age) {
      this.name=name;
      this.age=age;
      this.sayHi=function () {
        console.log("您好");
      };
    }
    //创建对象---->实例化一个对象,的同时对属性进行初始化

    /*
    * 共同点:都是函数,都可以创建对象,都可以传入参数
    *
    * 工厂模式:
    * 函数名是小写
    * 有new,
    * 有返回值
    * new之后的对象是当前的对象
    * 直接调用函数就可以创建对象
    *
    * 自定义构造函数:
    * 函数名是大写(首字母)
    * 没有new
    * 没有返回值
    * this是当前的对象
    * 通过new的方式来创建对象
    *
    *
    * */




    var per1=new Person("小红",20);

    var per2=createObject("小明",20);
  </script>
</head>
<body>


</body>
</html>

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

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>
    //面向对象的思想是----抽象的过程---->实例化的过程

    //小苏这个人,姓名,年龄,性别, 吃饭,打招呼,睡觉

    //自定义构造函数----->实例化对象
    function Person(name,age,sex) {
      this.name=name;
      this.age=age;
      this.sex=sex;
      this.eat=function () {
        console.log("吃大蒜拌臭豆腐加榴莲酱");
      };
    }
    //构造函数---->创建对象
    var per=new Person("小苏",38,"女");
    //per.eat();//吃
    //实例对象是通过构造函数来创建
    //实例对象会指向自己的构造函数(暂时理解,是错误的)
    //把这个对象的结构显示出来
    console.dir(per);
    console.dir(Person);


    //实例对象的构造器(构造函数)
    //实例对象的构造器是指向Person的,结果是true,所以,这个实例对象per就是通过Person来创建的
    console.log(per.constructor==Person);//

    console.log(per.__proto__.constructor==Person);
    console.log(per.__proto__.constructor==Person.prototype.constructor);



    //构造函数
    function Animal(name) {
      this.name=name;
    }
    //实例对象
    var dog=new Animal("大黄");
    console.dir(dog);//实例对象
    console.dir(Animal);//构造函数的名字

    console.log(dog.__proto__.constructor==Person);
    console.log(dog.__proto__.constructor==Animal);


    //判断这个对象是不是这种数据类型
    console.log(dog.constructor==Animal);

    console.log(dog instanceof Person);



    //总结:
    /*
    * 实例对象和构造函数之间的关系:
    * 1. 实例对象是通过构造函数来创建的---创建的过程叫实例化
    * 2.如何判断对象是不是这个数据类型?
    *  1) 通过构造器的方式 实例对象.构造器==构造函数名字
    *  2) 对象 instanceof 构造函数名字
    *  尽可能的使用第二种方式来识别,为什么?原型讲完再说
    *
    *
    *
    * */






  </script>
</head>
<body>


</body>
</html>

构造函数创建对象带来的问题

更优雅的工厂函数:构造函数

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

解析构造函数代码的执行

在上面的示例中,Person() 函数取代了 createPerson() 函数,但是实现效果是一样的。

这是为什么呢?

我们注意到,Person() 中的代码与 createPerson() 有以下几点不同之处:

  • 没有显示的创建对象
  • 直接将属性和方法赋给了 this 对象
  • 没有 return 语句
  • 函数名使用的是大写的 Person

而要创建 Person 实例,则必须使用 new 操作符。

以这种方式调用构造函数会经历以下 4 个步骤:

  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
  // return this
}

使用构造函数的好处不仅仅在于代码的简洁性,更重要的是我们可以识别对象的具体类型了。

在每一个实例对象中的_proto_中同时有一个 constructor 属性,该属性指向创建该实例的构造函数:

console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true

对象的 constructor 属性最初是用来标识对象类型的,

但是,如果要检测对象的类型,还是使用 instanceof 操作符更可靠一些:

console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true

总结:

  • 构造函数是根据具体的事物抽象出来的抽象模板
  • 实例对象是根据抽象的构造函数模板得到的具体实例对象
  • 每一个实例对象都具有一个 constructor 属性,指向创建该实例的构造函数
    • 注意: constructor 是实例的属性的说法不严谨,具体后面的原型会讲到
  • 可以通过实例的 constructor 属性判断实例和构造函数之间的关系
    • 注意:这种方式不严谨,推荐使用 instanceof 操作符,后面学原型会解释为什么

构造函数的问题
使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题:

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = function () {
    console.log('hello ' + this.name)
  }
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。

那就是对于每一个实例对象,type 和 sayHello 都是一模一样的内容,

每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

console.log(p1.sayHello === p2.sayHello) // => false

对于这种问题我们可以把需要共享的函数定义到构造函数外部:

function sayHello = function () {
  console.log('hello ' + this.name)
}

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = sayHello
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true

这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局命名空间冲突的问题。

你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:

var fns = {
  sayHello: function () {
    console.log('hello ' + this.name)
  },
  sayAge: function () {
    console.log(this.age)
  }
}

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = fns.sayHello
  this.sayAge = fns.sayAge
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true
console.log(p1.sayAge === p2.sayAge) // => true

至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。

但是代码看起来还是那么的格格不入,那有没有更好的方式呢?

3.原型(解决构造函数的问题)

更好的解决方案: prototype

Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。

这个对象的所有属性和方法,都会被构造函数的实例继承。

这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。

function Person (name, age) {
  this.name = name
  this.age = age
}

console.log(Person.prototype)

Person.prototype.type = 'human'

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

var p1 = new Person(...)
var p2 = new Person(...)

console.log(p1.sayName === p2.sayName) // => true

这时所有实例的 type 属性和 sayName() 方法,

其实都是同一个内存地址,指向 prototype 对象,因此就提高了运行效率。

构造函数、实例、原型三者之间的关系
在这里插入图片描述
任何函数都具有一个 prototype 属性,该属性是一个对象。

function F () {}
console.log(F.prototype) // => object

F.prototype.sayHi = function () {
  console.log('hi!')
}

构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数。

console.log(F.constructor === F) // => true

通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 proto

var instance = new F()
console.log(instance.__proto__ === F.prototype) // => true

实例对象可以直接访问原型对象成员:

instance.sayHi() // => hi!

总结:

  • 任何函数都具有一个 prototype 属性,该属性是一个对象
  • 构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数
  • 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 proto
  • 所有实例都直接或间接继承了原型对象的成员

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

了解了 构造函数-实例-原型对象 三者之间的关系后,接下来我们来解释一下为什么实例对象可以访问原型对象中的成员。

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

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

也就是说,在我们调用 person1.sayName() 的时候,会先后执行两次搜索:

  • 首先,解析器会问:“实例 person1 有 sayName 属性吗?”答:“没有。
  • ”然后,它继续搜索,再问:“ person1 的原型有 sayName 属性吗?”答:“有。
  • ”于是,它就读取那个保存在原型对象中的函数。
  • 当我们调用 person2.sayName() 时,将会重现相同的搜索过程,得到相同的结果。

而这正是多个对象实例共享原型所保存的属性和方法的基本原理。

总结:

  • 先在自己身上找,找到即返回
  • 自己身上找不到,则沿着原型链向上查找,找到即返回
  • 如果一直到原型链的末端还没有找到,则返回 undefined

实例对象读写原型对象成员

读取:

  • 先在自己身上找,找到即返回
  • 自己身上找不到,则沿着原型链向上查找,找到即返回
  • 如果一直到原型链的末端还没有找到,则返回 undefined

值类型成员写入(实例对象.值类型成员 = xx):

  • 当实例期望重写原型对象中的某个普通数据成员时实际上会把该成员添加到自己身上
  • 也就是说该行为实际上会屏蔽掉对原型对象成员的访问

引用类型成员写入(实例对象.引用类型成员 = xx):

  • 同上

复杂类型修改(实例对象.成员.xx = xx):

  • 同样会先在自己身上找该成员,如果自己身上找到则直接修改
  • 如果自己身上找不到,则沿着原型链继续查找,如果找到则修改
  • 如果一直到原型链的末端还没有找到该成员,则报错(实例对象.undefined.xx = xx)

更简单的原型语法

我们注意到,前面例子中每添加一个属性和方法就要敲一遍 Person.prototype 。

为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象:

function Person (name, age) {
  this.name = name
  this.age = age
}

Person.prototype = {
  type: 'human',
  sayHello: function () {
    console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
  }
}

在该示例中,我们将 Person.prototype 重置到了一个新的对象。

这样做的好处就是为 Person.prototype 添加成员简单了,但是也会带来一个问题,那就是原型对象丢失了 constructor 成员。

所以,我们为了保持 constructor 的指向正确,建议的写法是:

function Person (name, age) {
  this.name = name
  this.age = age
}

Person.prototype = {
  constructor: Person, // => 手动将 constructor 指向正确的构造函数
  type: 'human',
  sayHello: function () {
    console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
  }
}

原型对象的问题

  • 共享数组
  • 共享对象

如果真的希望可以被实例对象之间共享和修改这些共享数据那就不是问题。但是如果不希望实例之间共享和修改这些共享数据则就是问题。

一个更好的建议是,最好不要让实例之间互相共享这些数组或者对象成员,一旦修改的话会导致数据的走向很不明确而且难以维护。

原型对象使用建议

  • 私有成员(一般就是非函数成员)放到构造函数中
  • 共享成员(一般就是函数)放到原型对象中
  • 如果重置了 prototype 记得修正 constructor 的指向

原型对象中的方法可以相互调用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>

//    function Person(age) {
//      this.age=age;
//      this.sayHi=function () {
//        console.log("你好");
//        //打招呼的同时,直接调用吃的方法
//
//      };
//      this.eat=function () {
//        console.log("吃东西啦");
//        this.sayHi();
//      };
//    }
//    //实例对象的方法,是可以相互调用的
//
//    //实例化对象,并初始化
//    var per=new Person(20);
//    //调用方法
//    //per.sayHi();
//    per.eat();


    //原型中的方法,是可以相互访问的

    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();

    //原型对象中的方法,可以相互调用


  </script>
</head>
<body>


</body>
</html>

为内置对象添加原型的方法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>
    //为内置对象添加原型方法

//    var arr=new Array(10,20,30,40,50);
    //    arr.join("|");
    //    console.dir(arr);
    //
    //
    //    var str=new String("哦,唛嘎的");
    //    str.indexOf("哦");
    //    console.dir(str);
    //
    //
    //    var dt=new Date();
    //    dt.getFullYear();
    //    console.dir(dt);

    //实例中的方法如果没有,去创建该实例对象的构造函数的原型对象中找



    //我们能否为系统的对象的原型中添加方法,相当于在改变源码
    //我希望字符串中有一个倒序字符串的方法
    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
    };

    var arr=[100,3,56,78,23,10];
    arr.mySort();
    console.log(arr);


    String.prototype.sayHi=function () {
      console.log(this+"哈哈,我又变帅了");
    };

    //字符串就有了打招呼的方法
    var str2="你好";
    str2.sayHi();
  </script>
</head>
<body>
</body>
</html>

4.把局部变量变为全局变量

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>

    //函数的自调用---自调用函数

    //一次性的函数--声明的同时,直接调用了
//    (function () {
//      console.log("函数");
//
//
//    })();


    //页面加载后.这个自调用函数的代码就执行完了
//    (function (形参) {
//      var num=10;//局部变量
//    })(实参);
//    console.log(num);


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

    //如何把局部变量变成全局变量?
    //把局部变量给window就可以了



  </script>
</head>
<body>


</body>
</html>

5.产生随机数对象

所需引入的js文件:


//foreach的兼容代码
if (!Array.prototype.forEach) {

    Array.prototype.forEach = function(callback, thisArg) {

        var T, k;

        if (this == null) {
            throw new TypeError(' this is null or not defined');
        }

        // 1. Let O be the result of calling toObject() passing the
        // |this| value as the argument.
        var O = Object(this);

        // 2. Let lenValue be the result of calling the Get() internal
        // method of O with the argument "length".
        // 3. Let len be toUint32(lenValue).
        var len = O.length >>> 0;

        // 4. If isCallable(callback) is false, throw a TypeError exception. 
        // See: http://es5.github.com/#x9.11
        if (typeof callback !== "function") {
            throw new TypeError(callback + ' is not a function');
        }

        // 5. If thisArg was supplied, let T be thisArg; else let
        // T be undefined.
        if (arguments.length > 1) {
            T = thisArg;
        }

        // 6. Let k be 0
        k = 0;

        // 7. Repeat, while k < len
        while (k < len) {

            var kValue;

            // a. Let Pk be ToString(k).
            //    This is implicit for LHS operands of the in operator
            // b. Let kPresent be the result of calling the HasProperty
            //    internal method of O with argument Pk.
            //    This step can be combined with c
            // c. If kPresent is true, then
            if (k in O) {

                // i. Let kValue be the result of calling the Get internal
                // method of O with argument Pk.
                kValue = O[k];

                // ii. Call the Call internal method of callback with T as
                // the this value and argument list containing kValue, k, and O.
                callback.call(T, kValue, k, O);
            }
            // d. Increase k by 1.
            k++;
        }
        // 8. return undefined
    };
}


/**
 * Created by Administrator on 2017-08-18.
 */
//格式化日期的代码

//根据id获取元素的代码

//innerText和textContent的兼容

//获取第一个子元素的兼容

//获取最后一个子元素的兼容

/**
 * Created by Administrator on 2017/3/24.
 */

/**
 * 格式化日期
 * @param dt 日期对象
 * @returns {string} 返回值是格式化的字符串日期
 */
function getDates(dt) {
    var str = "";//存储时间的字符串
    //获取年
    var year = dt.getFullYear();
    //获取月
    var month = dt.getMonth() + 1;
    //获取日
    var day = dt.getDate();
    //获取小时
    var hour = dt.getHours();
    //获取分钟
    var min = dt.getMinutes();
    //获取秒
    var sec = dt.getSeconds();
    month = month < 10 ? "0" + month : month;
    day = day < 10 ? "0" + day : day;
    hour = hour < 10 ? "0" + hour : hour;
    min = min < 10 ? "0" + min : min;
    sec = sec < 10 ? "0" + sec : sec;
    str = year + "年" + month + "月" + day + "日 " + hour + ":" + min + ":" + sec;
    return str;
}
/**
 * 获取指定标签对象
 * @param id 标签的id属性值
 * @returns {Element}根据id属性值返回指定标签对象
 */
function my$(id) {
    return document.getElementById(id);
}
/**
 * 设置元素的文本内容
 * @param element 任意元素
 * @param text 任意文本内容
 */
function setInnerText(element, text) {
    if (typeof(element.textContent) == "undefined") {
        element.innerText = text;
    } else {
        element.textContent = text;
    }
}
/**
 * 获取元素的文本内容
 * @param element 任意元素
 * @returns {*} 任意元素中的文本内容
 */
function getInnerText(element) {
    if (typeof(element.textContent) == "undefined") {
        return element.innerText;
    } else {
        return element.textContent;
    }
}
/**
 * 获取父级元素中的第一个子元素
 * @param element 父级元素
 * @returns {*} 父级元素中的子级元素
 */
function getFirstElement(element) {
    if (element.firstElementChild) {
        return element.firstElementChild;
    } else {
        var node = element.firstChild;
        while (node && node.nodeType != 1) {
            node = node.nextSibling;
        }
        return node;
    }
}
/**
 * 获取父级元素中的最后一个子元素
 * @param element 父级元素
 * @returns {*} 最后一个子元素
 */
function getLastElement(element) {
    if (element.lastElementChild) {
        return element.lastElementChild;
    } else {
        var node = element.lastChild;
        while (node && node.nodeType != 1) {
            node = node.previousSibling;
        }
        return node;
    }
}
/**
 * 获取某个元素的前一个兄弟元素
 * @param element 某个元素
 * @returns {*} 前一个兄弟元素
 */
function getPreviousElement(element) {
    if (element.previousElementSibling) {
        return element.previousElementSibling
    } else {
        var node = element.previousSibling;
        while (node && node.nodeType != 1) {
            node = node.previousSibling;
        }
        return node;
    }
}
/**
 * 获取某个元素的后一个兄弟元素
 * @param element 某个元素
 * @returns {*} 后一个兄弟元素
 */
function getNextElement(element) {
    if (element.nextElementSibling) {
        return element.nextElementSibling
    } else {
        var node = element.nextSibling;
        while (node && node.nodeType != 1) {
            node = node.nextSibling;
        }
        return node;
    }
}
/**
 * 获取某个元素的所有兄弟元素
 * @param element 某个元素
 * @returns {Array} 兄弟元素
 */
function getSiblings(element) {
    if (!element)return;
    var elements = [];
    var ele = element.previousSibling;
    while (ele) {
        if (ele.nodeType === 1) {
            elements.push(ele);
        }
        ele = ele.previousSibling;
    }
    ele = element.nextSibling;
    while (ele) {
        if (ele.nodeType === 1) {
            elements.push(ele);

        }
        ele = ele.nextSibling;
    }
    return elements;
}
/**
 * 返回当前浏览器是什么类型的浏览器
 */
function userBrowser(){
    var browserName=navigator.userAgent.toLowerCase();
    if(/msie/i.test(browserName) && !/opera/.test(browserName)){
        console.log("IE");
    }else if(/firefox/i.test(browserName)){
        console.log("Firefox");
    }else if(/chrome/i.test(browserName) && /webkit/i.test(browserName) && /mozilla/i.test(browserName)){
        console.log("Chrome");
    }else if(/opera/i.test(browserName)){
        console.log("Opera");
    }else if(/webkit/i.test(browserName) &&!(/chrome/i.test(browserName) && /webkit/i.test(browserName) && /mozilla/i.test(browserName))){
        console.log("Safari");
    }else{
        console.log("不知道什么鬼!");
    }
}



//为任意一个元素绑定事件:元素,事件类型,事件处理函数
function addEventListener(element,type,fn) {
    if(element.addEventListener){
        //支持
        element.addEventListener(type,fn,false);
    }else if(element.attachEvent){
        element.attachEvent("on"+type,fn);
    }else{
        element["on"+type]=fn;
    }
}
//为任意的一个元素解绑某个事件:元素,事件类型,事件处理函数
function removeEventListener(element,type,fn) {
    if(element.removeEventListener){
        element.removeEventListener(type,fn,false);
    }else if(element.detachEvent){
        element.detachEvent("on"+type,fn);
    }else{
        element["on"+type]=null;
    }
}

/**
 * 获取的是页面向上或者向左卷曲出去的距离的值,返回的是对象
 * @returns {{top: (Number|number), left: (Number|number)}}
 */
function getScroll() {
    return {
        top: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop || 0,
        left: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft||0
    };
}

随机数代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>

    //通过自调用函数产生一个随机数对象,在自调用函数外面,调用该随机数对象方法产生随机数
    (function (window) {
      //产生随机数的构造函数
      function Random() {
      }
      //在原型对象中添加方法
      Random.prototype.getRandom = function (min,max) {
        return Math.floor(Math.random()*(max-min)+min);
      };
      //把Random对象暴露给顶级对象window--->外部可以直接使用这个对象
      window.Random=Random;
    })(window);
    //实例化随机数对象
    var rm=new Random();
    //调用方法产生随机数
    console.log(rm.getRandom(0,5));


    //全局变量
  </script>
</head>
<body>


</body>
</html>

6.案例随机小方块

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		.map{
			width: 800px;
			height: 600px;
			background-color: #CCC;
			position: relative;
		}
	</style>
</head>
<body>
<div class="map"></div>
<script src="common.js"></script>
<script>
	//产生随机数对象
	(function (window) {
		function Random() {

		}
		Random.prototype.getRandom=function (min,max) {
			return Math.floor(Math.random()*(max-min)+min);
		};
		//把局部对象暴露给window顶级对象,就成了全局对象
		window.Random=new Random();
	})(window);//自调用构造函数的方式,分号一定要加上

	//产生小方块对象
	(function (window) {
		//选择器的方式来获取元素对象
		var map=document.querySelector(".map");

		//小方块的构造函数
		function Food(width,height,color) {
			this.width=width||20;//默认的小方块的宽
			this.height=height||20;//默认的小方块的高
			//横坐标,纵坐标
			this.x = 0;//横坐标随机产生
			this.y = 0;//纵坐标随机产生
			this.color=color;//小方块的背景颜色
			this.element=document.createElement("div");//小方块的元素
		}
		//初始化小方块的显示效果及位置--显示在地图上
		Food.prototype.init=function (map) {
			//设置小方块的样式
			var div=this.element;
			div.style.position="absolute";//脱离文档流
			div.style.width = this.width+"px";
			div.style.height = this.height+"px";
			div.style.backgroundColor=this.color;
			//把小方块加到map地图中
			map.appendChild(div);
			this.render(map);
		} ;
		//产生随机位置
		Food.prototype.render=function (map) {
			//随机产生横纵坐标
			var x=Random.getRandom(0,map.offsetWidth/this.width)*this.width;
			var y=Random.getRandom(0,map.offsetHeight/this.height)*this.height;
			this.x = x;
			this.y = y;
			var div=this.element;
			div.style.left = this.x+"px";
			div.style.top = this.y+"px";
		};
		//实例化对象
		var fd=new Food(20,20,"green");
		fd.init(map);

	})(window);
</script>
</body>
</html>

在这里插入图片描述

7.总结

本期学习到此结束,下期我们来做“贪吃蛇”案例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值