JS 高级特性

什么是对象
  • 万物皆对象

  • 我们可以从两个层次来理解
    (1)对象是单个事物的抽象
    (2)对象是一个容器,封装来属性(property)和方法(method)
    属性:对象的状态
    方法:对象的行为

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

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

什么是面向对象
  • 面向对象编程 ---- Object Oriented Programming 简称 OOP,是一种编程开发思想
  • 它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟

面向对象与面向过程对比

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

面向对象的特性

  • 封装
  • 继承
  • 多态(抽象)

总结

  • 在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接收信息,处理数据、发出信息等任务
  • 因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程,更适合多人合作的大型软件项目
面向对象的设计思想
  • 抽象出 class (构造函数)
  • 根据 Class(构造函数) 创建 Instance ( 实例)
  • 指挥 Instance 得出结果
创建对象的几种方法
  • new Object() 构造函数
    // 简单方式 new Object()
    var person = new Object();
    person.name = "tin";
    person.age = 18;
    person.sayName = function() {
        console.log(this.name)
    }
  • 对象字面量 {}
    // 对象字面量化简
    var person = {
        name: "tin",
        age = 19,
        sayName : function(){
            console.log(this.name)
        }
    }
  • 工厂函数
    // 工厂函数
    function createPerson(name,age){
        //添加一个新对象
        var person = new Object();
        person.name = name;
        person.age = age;
        person.sayName = function(){
            console.log(this.name);
        }
        // 必须有返回值
        return person
    }
    //生产真正的对象
    var person1 = createPerson("bob",17);
    var person2 = createPerson("tin",18);
  • 自定义构造函数
    // 自定义构造函数
    function Person(name,age){
        this.name = name;
        this.age = age;
        this.sayName = function(){
            console.log(this.name)
        }
    }

    // 生成对象实例
    var person1 = new Person("tin",19);
    var person2 = new Person("bob",18);

    person1.sayName();
    person2.sayName();

new 关键字的用途
1 创建一个新对象
2 将函数内部的 this 指向这个新对象
3 执行构造函数内部的代码
4 将新对象作为返回值
通过构造函数生成的实例是可以找到自己当初的构造函数的
constructor 属性 构造器 构造函数
每个对象的 constructor 属性值就是生成这个对象的构造函数
判断一个对象的具体类型,需要使用 instance of 进行判断

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

  • 构造函数是根据具体事物抽象出来的抽象模版
  • 实例对象是根据抽象的构造函数模版得到的具体实例对象
  • 每一个实例对象都通过一个 constructor 属性,指向创建该实例的构造函数
    注意:constructor 是实例的属性的说法不严谨,具体后面的原型会讲到
  • 可以通过 constructor 属性判断实例和构造函数之间的关系
    注意:这种方式不严谨,推荐使用 instanceof 操作符
静态成员和实例成员
  • 使用构造函数方法创建对象时,可以给构造函数和创建的实例对象添加属性和方法,这些属性和方法都叫做成员
  • 实例成员:在构造函数内部添加给 this 的成员,属于实例对象的成员,在创建实例对象后必须由对象调用。
  • 静态成员:添加给构造函数自身的成员,只能使用构造函数调用,不能使用生成的实例对象调用

构造函数的问题

  • 浪费内存
    // 解决办法1:将公共的函数提取到构造函数之外
    function sayName(){
        console.log(this.name);
    }
    // 解决办法2:将多个公共的函数封装到一个对象
    var fns = {
        sayName: function(){
            console.log(this.name);
        },
        sayAge: function(){
            console.log(this.age)
        }
    }

原型对象

  • 使用原型对象可以更好的解决构造函数的内存浪费问题

prototype 原型对象

  • 任何函数都具有一个 prototype 属性,该属性是一个对象
  • 可以在原型对象上添加属性和方法
  • 构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在的函数
  • 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 prototype
  • 实例对象可以直接访问原型对象成员
    // 定义构造函数
    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    // 获取构造函数的 prototype 属性
    console.log(Person.prototype);
    // 属性值是一个对象,通常叫做原型对象
    // 对象内部可以添加一些属性和方法
    Person.prototype.type = "human";
    Person.prototype.sayHi = function(){
        console.log("hello");
    };

    // 构造函数的原型对象上面都默认有一个 constructor 属性
    console.log(Person.prototype.constructor);

    // 创建实例对象
    var p1 = new Person("mike",19);
    // 所有对象都有一个 _proto_ 的属性,是一个指针,指向的就是生成实例对象的原型对象
    console.log(p1._proto_)

    // _proto_ 属性并不是一个标准的属性,是浏览器自己根据语法自动生成
    // 在真正的开发中,是不会书写_proto_ 属性的
    p1.sayHi();
构造函数、实例、原型对象三者之间的关系.png

解决方法

  • JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向构造函数的原型对象
  • 这个原型对象的所有属性和方法,都会被构造函数的实例对象所拥有
  • 因此我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上
  • 解决内存浪费问题
原型链
原型链.png

原型链查找机制
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标时具有给定名字的属性

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

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

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

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

  • 同上

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

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

更简单的原型语法
前面在原型对象每添加一个属性和方法就要书写一遍 Person.prototype。
为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,将 Person.prototype 重置到一个新的对象。
注意:原型对象会丢失 constructor 成员,所以需要手动将 constructor 指向正确的构造函数。

    Person.prototype = {
        constructor : Person, // 需要手动将 constructor 指向正确的构造函数
        type : "human",
        sayName : function(){
            console.log(this,name)
        }
    }

建议
在定义构造函数时,可以根据成员的功能不同,分别进行设置

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

内置构造函数的原型对象
所有函数都有 prototype 属性对象
Javascript中的内置构造函数也有 prototype 原型对象属性:

  • Object.prototype
  • Function.prototype
  • Array.prototype
  • String.prototype
  • Number.prototype

继承

封装的构造函数就是用来创建一类对象
继承指的是 类型和类型之间的继承
原型对象可以将自己的属性和方法继承给将来的实例对象使用

函数的 call 方法 6069

call 方法在调用函数的时候,有两个功能
1 更改函数内部的 this执行
2 调用函数执行内部代码
参数:第一个参数用来指定 this,第二个及以后,就是传的实参

构造函数的原型方法继承

  • 拷贝继承 for in
  • 原型继承
student.prototype = new person();
student.prototype.constructor = student;

组合继承
属性在构造函数内部继承,方法通过原型继承

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

    person.prototype.sayHi = function(){
        console.log(this.name + ":ni hao");
    }

    function student(name,age,score){
        person.call(this,name,age);
        this.score = score;
    }

    student.prototype = new person();
    student.prototype.constructor = student;

    var s1 = new student("zzj",18,100);
    console.log(s1);
    s1.sayHi();
函数声明和函数表达式
// 函数声明
function fun(){
  console.log(1);
};
// 函数表达式 --- 将函数赋值给一个变量,可以是一个匿名函数
var fn = function (){
  console.log(2);
};

函数声明与函数表达式的区别

  • 函数声明必须有名字
  • 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
  • 函数表达式类似于变量赋值
  • 函数表达式可以没有名字,例如匿名函数
  • 函数表达式没有函数提升,在执行阶段创建,必须在表达式执行之后才可以调用

函数也是对象

  • 函数本身也是一种对象,可以调用属性和方法
var fn = new Function('var a = "1";console.log(a)');
fun(2)

函数的调用和this 5715

    // 1 普通函数 是通过给函数名或者变量名添加()方式执行
    // 内部的 this 默认指向 window
    function fun(){
        console.log(1);
    }
    fun();
    // 2 构造函数,是通过 new 关键字进行调用
    // 内部的 this 指向的是将来创建的实例对象
    function Person(name){
        this.name = name;
    }
    var p1 = new Person("zs");
    // 3 对象中的方法,是通过对象打点调用函数,然后加小括号
    // 内部的 this 默认指向的是调用的对象自己
    var o = {
        sayHi : function(){
            console.log("hhh")
        }
    }
    o.sayHi();
    // 4 事件函数,不需要加特殊的符号,只要事件被触发,会自动执行函数
    // 事件函数的内部 this 指向的是事件源
    document.onclick = function(){
        console.log("hhh")
    }
    // 5 定时器和延时器中的函数,不需要加特殊符号,只要执行后,在规定的时间自动执行
    // 默认内部的 this 指向的是 window
    setInterval(function(){
        console.log("hhh")
    },1000)


  • 函数的调用方式决定了this指向的不同:


    image.png

call

  • call() 方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)
  • 注意:该方法的作用和apply()方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组
  • 语法
    fun.call(thisArg,arg1,arg2,...)
  • thisArg
    在fun函数运行时指定的this值
    如果指定了null或者undefined则内部this指向window
  • arg1,arg2
    指定的参数列表‘
    var o = {
        0: 10,
        1: 20,
        2: 30
    };

    Array.prototype.push.call(o,40);



apply

1 功能:第一个可以指定函数的 this,第二个可以执行函数并传参
2 参数:第一个参数,传入一个指定让 this 指向的对象,第二个参数是函数的参数组成的数组
3 返回值:就是函数自己的返回值

    // 定义一组数组,利用 apply 方法,可以将它拆开进行操作
    var arr = [1,3,4,56,8];

    // 想借用一些现在内置在 js 中的方法
    // 利用 apply 方法,将数组传给 max 的第二个参数
    console.log(Math.max.apply(Math,arr))


bind
1 功能:第一个可以指定函数的 this,bind 方法不能执行函数,但是可以传参
2 参数:第一个参数,传入一个指定让 this 指向的对象,第二个参数及以后,是函数参数的列表
3 返回值:返回一个新的指定了 this 的函数,也可以叫绑定函数

    // 想修改的是定时器的函数内部的 this
    var o = {
        name: "zs",
        age: 18,
        s: function(){
            setInterval(
                function(){
                    console.log(this.age);
                }.bind(this),1000);
        }
    }

高阶函数

  • 函数可以作为参数
  • 函数可以作为返回值
    // 1 函数作为另一个函数的参数
    // 定义一个函数,吃饭的函数,吃完饭之后,可以做其他的事情,看电影,聊天,看书
    function eat(fn) {
        console.log("吃饭");
        // 接下来的事情不固定
        fn();
    }
    eat(function () {
        console.log("看电影");
    })

    // 2. 函数作为一个函数的返回值
    // 需求:通过同一段代码实现以下效果
    // 输出 100 + m
    // 输出 1000 + m
    // 输出 10000 + m
    function outer(n){
        return function inner(m){
            console.log(m + n);
        }
    }
    // 在外部执行 inner 函数
    // 100 + m
    var fun = outer(100);
    fun(3);
    // 1000 + m
    var fun1 = outer(1000)
    fun(3);
闭包
  • 一个函数和对其周围状态的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

  • 函数定义时天生就能记住自己生成的作用域环境和函数自己,将它们形成一个密闭的环境,这就是闭包。不论函数以任何方式在任何地方进行调用,都会回到自己定义时的密闭环境进行执行。


    闭包.png

观察闭包

  • 从广义上来说,定义在全局的函数也是一个闭包,只是我们没办法将这样的函数拿到更外面的作用域进行调用,从而观察闭包的特点,
  • 闭包是天生存在的,不需要额外的结构,但是我们为了方便观察闭包的特点,需要利用一些特殊结构将一个父函数内部的子函数拿到父函数外部进行调用,从而观察闭包的存在。

闭包的用途

  • 可以在函数外部读取函数内部成员
  • 让函数内成员始终存活在内存中
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 一、 JS面向对象编程 1、 面向对象介绍 什么是对象? Everything is object (万物皆对象)...
    宠辱不惊丶岁月静好阅读 824评论 0 2
  • 搭建页面 index.css 分析对象 游戏对象 蛇对象 食物对象 食物对象 1.创建构造函数Food,设置属性 ...
    amanohina阅读 520评论 0 0
  • 继承 一、混入式继承 二、原型继承 利用原型中的成员可以被和其相关的对象共享这一特性,可以实现继承,这种实现继承的...
    magic_pill阅读 1,049评论 0 3
  • 学习目标: 理解面向对象开发思想 掌握 JavaScript 面向对象开发相关模式 掌握在 JavaScript ...
    金桔柠檬加冰阅读 400评论 0 0
  • 1,javascript 基础知识 Array对象 Array对象属性 Arrray对象方法 Date对象 Dat...
    Yuann阅读 889评论 0 1