美文网首页
黑马视频-JavaScript高级 1

黑马视频-JavaScript高级 1

作者: 璎珞纨澜 | 来源:发表于2019-08-26 15:57 被阅读0次

1. JavaScript 基础介绍

1.1 JavaScript 是什么?

  • 解释执行:轻量级解释下的,是 JIT 编译型的程序设计语言
  • 语言特点:动态,头等函数(First-class Function)
    • 又称函数是 JavaScript 中的一等公民
  • 执行环境:在宿主环境下(host environment)下运行,浏览器是最常见的 JavaScript 宿主环境
    • 但是在很多非浏览器环境中也是用 JavaScript,例如 node.js
  • 编程范式:基于原型,多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如:函数式编程)编程风格

JS 分为三个部分

组成部分 说明
Ecmascript 描述了该语言的语法和基本对象
DOM 描述了处理网页内容的方法和接口
BOM 描述了与浏览器进行交互的方法和接口

1.2 基本概念

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

1.3 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 运行分为两个阶段:

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

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


2. JavaScript 面向对象编程

2.1 编程思想

  • 面向过程:所有的事情都是亲力亲为,注重的是过程。
  • 面向对象:提出需求,找对象,对象解决,注重的是结果。
    • 面向对象的编程思想:根据需求,抽象出相关的对象,总结对象的特征和行为,把特征变成属性,把行为变成方法。然后定义(js)构造函数,实例化对象,通过对象调用属性和方法,完成相应的需求。
    • js 不是一门面向对象的语言,是基于对象的语言,js可以模拟面向对象。

2.2 面向对象的特性

  • 封装:就是代码的封装,把一些特征和行为封装在对象中
  • 继承:类与类之间的关系。js中没有类的概念,js有构造函数的概念,是可以有继承的,js的继承是基于原型的
  • 多态:同一个行为,针对不同的对象,产生了不同的效果

2.3 创建对象的三种方式

  1. 字面量的方式
var per1 = {
    name:"小张",
    age:20,
    sex:"男",
    readBook:function(){
        console.log("西游记")
    }
}
  1. 调用系统的构造函数方法
var per2 = new Object()
per2.name="小张"
per2.age=20
per2.sex="男"
per2.readBook=function(){
    console.log("西游记")
}

以上两种方式都是不能确定创建出来的对象是什么类型的

  1. 自定义构造函数
function Person(name,age,sex){
    this.name = name
    this.age = age
    this.sex =sex
    this.readBook= function () {
        console.log("西游记")
    }
}
var per = new Person("小张",20,"男")
console.log(per instanceof Person)

结果输出 true

  1. 工厂模式创建对象
function createObejct(name, age, sex) {
    var obj = new Object()
    obj.name = name
    obj.age = age
    obj.sex = sex
    obj.readBook = function(){
        console.log("西游记")
    }
    return obj
}
var per1=createObejct("小明",20,"男")

自定义构造函数与工厂模式的共同点与不同点:

  • 共同点:都是函数,都可以创建对象,都可以传入参数
  • 不同点:
    • 工厂模式函数名是小写,自定义构造函数函数名是大写
    • 工厂模式函数内部 new 对象并返回该对象,自定义构造函数内部没有new对象返回
    • 工厂模式 new 之后的对象是当前的对象,自定义构造函数 this 是当前的对象
    • 工厂模式是直接调用函数就可以创建对象,自定义构造函数通过 new 的方式来创建对象

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

面向对象的思想是:抽象的过程 --> 实例化的过程
自定义构造函数 --> 使用构造函数实例化对象

function Animal(name) {
    this.name = name
}
var dog = new Animal("大黄")
console.dir(dog)
console.dir(Animal)
执行结果
console.log(dog instanceof Animal) // true
console.log(dog.constructor == Animal) // true
console.log(dog.__proto__.constructor == Animal) // true
console.log(dog.__proto__.constructor == Animal.prototype.constructor) // true

总结:

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

2.5 原型的引入

首先我们来看构造函数创建对象带来的问题:

function Person(name,age,sex){
    this.name = name
    this.age = age
    this.sex = sex
    this.readBook = function () {
        console.log("西游记")
    }
}
var per1 = new Person("小张",20,"男")
var per2 = new Person("小杨",25,"女")
per1.readBook()
per2.readBook()
console.log(per1.readBook == per2.readBook)
执行结果

为什么 per1 和 per2 的 readBook 不相等呢?是因为使用构造函数每创建一个对象就会开辟一块内存空间,而这个对象指向自己对应的那块内存空间。如果我new了100个对象,那么对应的就有一百块内存空间中有一样的 readBook 方法。这样就开辟了大量的控件,浪费内存。那么我们通过原型来解决这个问题。

function Person(name,age,sex){
    this.name = name
    this.age = age
    this.sex = sex
}
Person.prototype.readBook = function(){
    console.log("西游记")
}
var per1 = new Person("小张",20,"男")
var per2 = new Person("小杨",25,"女")
per1.readBook()
per2.readBook()
console.log(per1.readBook == per2.readBook)
image.png
  • __proto__prototype 都是原型对象,对象的引用指向相同。
  • 实例对象中的属性 __proto__,不是标准的属性,是浏览器使用的。有的浏览器支持,有的浏览器不支持该属性,例如IE8不支持,而火狐和谷歌是支持的。
  • 构造函数中的属性prototype,是标准属性,程序员使用的。
  • 原型的作用:解决数据共享,节省内存空间。

2.6 构造函数、实例对象和原型对象的关系

通过下面这张图来解释这三者的关系:


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

2.7 利用原型共享数据

不需要共享的数据写在构造函数中,需要共享的数据写在原型中。

function Student(name,age,sex){
    this.name = name
    this.age = age
    this.sex = sex
}
Student.prototype.major = "计算机"
Student.prototype.teacher = "王老师"
Student.prototype.study = function(){
    console.log("学Javascript")
}
Student.prototype.readBook = function(){
    console.log("看编程书")
}
var stu = new Student("小张",20,"男")
console.dir(Student)
console.dir(stu)
执行结果

上面的代码还可以将 prototype 写成一个对象,但是需要在原型对象中需要手动添加构造器为当前的构造函数:

function Student(name,age,sex){
    this.name = name
    this.age = age
    this.sex = sex
}
Student.prototype = {
    constructor: Student, //需要手动添加构造器
    major: "计算机",
    teacher: "王老师",
    study: function(){
        console.log("学Javascript")
    },
    readBook: function(){
    console.log("看编程书")
    }
}
var stu = new Student("小张",20,"男")
console.dir(Student)
console.dir(stu)

2.8 原型中的方法是可以相互调用的

我们知道实例对象的方法是可以相互调用的,像这样:

function Person(name,age,sex){
    this.name = name
    this.age = age
    this.sex = sex
    this.readBook = function () {
        console.log("西游记")
        this.writeBlog()
    }
    this.writeBlog = function() {
        console.log("写观后感")
    }
}
var per = new Person("小张",20,"男")
per.readBook()
执行结果

如果把方法定义在原型中,能不能相互调用呢?
原型中的方法是可以相互调用的。

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.8 实例对象使用属性和方法层层的搜索

实例对象的用到的属性或者方法先在实例对象中找,找到了则直接使用,如果在实例对象中找不到就会去实例对象的__proto__指向的构造函数的原型对象 prototype 中找,找到了则使用,如果原型对象也没有就会报错不存在。

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

我们能否为系统的对象的原型添加方法,这相当于在改变源码。
比如,我希望字符串中有一个倒序字符串的方法,就可以直接在 String 内置对象的原型对象上添加自己的 myReverse 方法:

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
          }
      }
  }
}
var arr=[100,3,56,78,23,10]
arr.mySort()
console.log(arr)

系统内置对象的属性和方法可能不满足现在需求,所以,为了方便开发,可以通过原型的方式加入属性或者方法。

相关文章

网友评论

      本文标题:黑马视频-JavaScript高级 1

      本文链接:https://www.haomeiwen.com/subject/ncltectx.html