自学JavaScript第二天- JS 进阶: 对象 函数

  • 对象进阶
    • 构造函数
    • 使用类
    • 类的继承
    • 静态方法
  • 函数进阶
    • 方法
      • 装饰器
    • 高阶函数
      • map / reduce
      • filter
      • sort
      • every
      • find
      • findIndex
      • forEach
    • 箭头函数
    • 生成器

对象进阶

面向对象有两个基本概念:

  • 类:类是对象的类型模板,例如,定义 Student 类来表示学生,类本身是一种类型, Student 表示学生类型,但不表示任何具体的某个学生;
  • 实例:实例是根据类创建的对象,例如,根据 Student 类可以创建出 xiaoming 、 xiaohong 、 xiaojun 等多个实例,每个实例表示一个具体的学生,他们全都属于 Student 类型。

不过,在老版本的JavaScript中,这个概念需要改一改。老版本JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。基于原型实现对象模型比较简单,但是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要大量代码,且需要正确实现原型链。于是 ES6 开始 class 被正式引入 js。

js 使用 class 关键字来定义一个类,类体在一对花括号 {} 中。需注意的是,由于不是所有的主流浏览器都支持ES6的 class,所以如果需要使用 class,可以试试Babel这个工具将 class 代码转换为传统的 prototype 代码。

构造函数

由类创建对象时自动执行的方法就是构造函数,js 的类在定义时可以定义特殊方法 constructor() 这个方法就是构造函数。

class ClassName {constructor() { ... }
}

使用类

当类定义好后,可以使用 new 关键字来创建类的对象。对象就是类的实例化,当创建对象时,会自动执行其构造函数。

类的继承

js 类继承使用 extends 关键字。

// 基类
class Animal {// eat() 函数// sleep() 函数
};//派生类
class Dog extends Animal {// bark() 函数
};

使用 super() 方法调用父类的构造函数。可以通过在构造函数中调用 super() 方法,调用父类的构造方法,来访问父类的属性和方法,继承父类的属性和方法。

静态方法

使用 static 关键字修饰方法,就成为静态方法,也叫类方法。它属于类,但不属于对象。可以使用 类名.静态方法名 来调用静态方法。静态方法不能在对象上使用,只能在类中使用。

函数进阶

方法

对象内部的函数成员(即绑定到对象的函数)就是方法。使用上和函数类似,不同的地方在于 this 关键字。this 是一个特殊变量,它始终指向当前对象。当函数没有自身对象时 this 就是全局对象,即 window 对象。比较坑爹的是,下面这个例子即使拿到函数再调用时,this 也是指向的 window

function getAge() {var y = new Date().getFullYear();return y - this.birth;
}var xiaoming = {name: '小明',birth: 1990,age: getAge
};
var fn = xiaoming.age; // 先拿到xiaoming的age函数
fn(); // NaN

要保证 this 指向正确,必须用 obj.xxx() 的形式调用!由于这是一个巨大的设计错误,要想纠正可没那么简单。ECMA决定,在strict模式下让函数的 this 指向 undefined ,因此,在strict模式下,上例中 fn() 会得到一个错误 Uncaught TypeError: Cannot read property 'birth' of undefined,将此问题暴漏出来。另外在重构函数中也有类似的问题

var xiaoming = {name: '小明',birth: 1990,age: function () {function getAgeFromBirth() {var y = new Date().getFullYear();return y - this.birth;}return getAgeFromBirth();}
};xiaoming.age(); // Uncaught TypeError: Cannot read property 'birth' of undefined

原因是 this 指针只在 age 方法的函数内指向 xiaoming ,在函数内部定义的函数, this 又指向 undefined 了!(在非strict模式下,它重新指向全局对象 window !)。修复的办法是用一个 that 变量首先捕获 this

var xiaoming = {name: '小明',birth: 1990,age: function () {var that = this; // 在方法内部一开始就捕获thisfunction getAgeFromBirth() {var y = new Date().getFullYear();return y - that.birth; // 用that而不是this}return getAgeFromBirth();}
};
xiaoming.age(); // 25

也可以使用函数本身的 apply 方法来手动控制 this 指向的对象。它接收两个参数,第一个参数就是需要绑定的 this 变量,第二个参数是 Array ,表示函数本身的参数。

function getAge() {var y = new Date().getFullYear();return y - this.birth;
}var xiaoming = {name: '小明',birth: 1990,age: getAge
};xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

或是 call 方法,区别在于参数按照顺序传入。对普通函数调用,我们通常把 this 绑定为 null。

Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5

装饰器

利用 apply() ,我们还可以动态改变函数的行为。

var count = 0;
var oldParseInt = parseInt;        // 保存原函数window.parseInt = function () {count += 1;return oldParseInt.apply(null, arguments); // 调用原函数
};

高阶函数

JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数

function add(x, y, f) {return f(x) + f(y);
}

map / reduce

map 就是映射,可以将若干元素以一一对应的关系传入函数中进行处理。由于 map() 方法定义在JavaScript的 Array 中,我们调用 Array 的 map() 方法,传入我们自己的函数,就得到了一个新的 Array 作为结果。注意: map() 传入的参数是 pow ,即函数对象本身。

function pow(x) {return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var results = arr.map(pow);
console.log(results);       // [1, 4, 9, 16, 25, 36, 49, 64, 81]
// 或者一些复杂的函数可以简单化
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']

再看reduce的用法。Array的 reduce() 把一个函数作用在这个 Array 的 [x1, x2, x3…] 上,这个函数必须接收两个参数, reduce() 把结果继续和序列的下一个元素做累积计算,其效果就是

[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

比方说对一个 Array 求和,就可以用 reduce 实现

var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {return x + y;
}); // 25

filter

filter也是一个常用的操作,它用于把 Array 的某些元素过滤掉,然后返回剩下的元素。和 map() 类似, Array 的 filter() 也接收一个函数。和 map() 不同的是, filter() 把传入的函数依次作用于每个元素,然后根据返回值是 true 还是 false 决定保留还是丢弃该元素。

var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {return x % 2 !== 0;
});
r; // [1, 5, 9, 15]

可见用 filter() 这个高阶函数,关键在于正确实现一个“筛选”函数。filter() 接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数,表示 Array 的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身

var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {console.log(element); // 依次打印'A', 'B', 'C'console.log(index); // 依次打印0, 1, 2console.log(self); // self就是变量arrreturn true;
});

sort

JavaScript的 Array 的 sort() 方法就是用于排序的,但是排序结果可能让你大吃一惊

// 看上去正常的结果:
['Google', 'Apple', 'Microsoft'].sort(); // ['Apple', 'Google', 'Microsoft'];// apple排在了最后:
['Google', 'apple', 'Microsoft'].sort(); // ['Google', 'Microsoft", 'apple']// 无法理解的结果:
[10, 20, 1, 2].sort(); // [1, 10, 2, 20]

第二个排序把 apple 排在了最后,是因为字符串根据ASCII码进行排序,而小写字母 a 的ASCII码在大写字母之后。第三个排序结果是因为 Array 的 sort() 方法默认把所有元素先转换为String再排序,结果 ‘10’ 排在了 ‘2’ 的前面,因为字符 ‘1’ 比字符 ‘2’ 的ASCII码小。

sort() 方法的默认排序规则很坑,幸运的是,它是一个高阶函数,可以接收一个比较函数来实现自定义排序。通常规定,对于两个元素 x 和 y ,如果认为 x < y ,则返回负值 ,如果认为 x == y ,则返回 0 ,如果认为 x > y ,则返回正值。这样按照数字大小排序应该为

var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {if (x < y) return -1 ;if (x > y) return 1;return 0;
});
console.log(arr);   // [1, 2, 10, 20]

需要注意的是,sort() 方法会直接对 Array 对象进行修改。

every

every() 方法可以判断数组的所有元素是否满足测试条件,类似于 filter ,只是不进行筛选而是根据条件返回 True 和 False。

find

find() 方法可以用于查找符合条件的第一个元素,找到返回元素,否则返回 undefined

findIndex

findIndex() 和 find() 类似,也是查找符合条件的第一个元素,不同之处在于 findIndex() 会返回这个元素的索引,如果没有找到,返回 -1

forEach

forEach() 和 map() 类似,它也把每个元素依次作用于传入的函数,但不会返回新的数组。 forEach() 常用于遍历数组,因此,传入的函数不需要返回值

箭头函数

ES6标准新增了一种新的函数:Arrow Function(箭头函数)。箭头之前的部分是函数参数,之后的部分是函数体。例如

function (x) {return x * x;
}
// 等价于
(x) => { return x * x }
// 如果只有一个参数,可以省略参数的括号
x => { return x * x }
// 如果函数体只有一条语句,则可以省略函数体的花括号和 return,能够自动返回其值
x => x * x

箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连 { … } 和 return 都省略掉了。还有一种可以包含多条语句,这时候就不能省略 {… } 和 return。如果参数不是一个,就需要用括号 () 括起来

// 两个参数:
(x, y) => x * x + y * y// 无参数:
() => 3.14// 可变参数:
(x, y, ...rest) => {var i, sum = x + y;for (i=0; i<rest.length; i++) {sum += rest[i];}return sum;
}

需注意返回一个对象,也需要使用括号括起来

x => { foo: x }      // SyntaxError
// 应写为
x => ({ foo: x })

箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的 this 是词法作用域,由上下文确定。箭头函数完全修复了 this 的指向, this 总是指向词法作用域,也就是外层调用者 obj

var obj = {birth: 1990,getAge: function () {// 省略了 var that = this;var b = this.birth; // 1990var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象return fn();}
};
obj.getAge(); // 25

生成器

generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。它借鉴了Python的generator的概念和语法。generator和函数不同的是,generator由 function* 定义(注意多出的 * 号),并且,除了 return 语句,还可以用 yield 返回多次

function* foo(x) {yield x + 1;yield x + 2;return x + 3;
}

例如编写一个产生斐波那契数列的生成器函数

function* fib(max) {vart,a = 0,b = 1,n = 0;while (n < max) {yield a;[a, b] = [b, a + b];n ++;}return;
}

直接调用一个generator和调用函数不一样, fib(5) 仅仅是创建了一个generator对象,还没有去执行它。

调用generator对象有两个方法

  • 一是不断地调用generator对象的 next() 方法。next() 方法会执行generator的代码,然后,每次遇到 yield x; 就返回一个对象 {value: x,done: true/false} ,然后“暂停”。返回的 value 就是 yield 的返回值, done 表示这个generator是否已经执行结束了。如果 done 为 true ,则 value 就是 return 的返回值。当执行到 done 为 true 时,这个generator对象就已经全部执行完毕,不要再继续调用 next() 了
  • 第二个方法是直接用 for … of 循环迭代generator对象,这种方式不需要我们自己判断 done

自学JavaScript第二天- JS 进阶: 对象 函数相关推荐

  1. 自学JavaScript第一天- JS 基础

    自学JavaScript第一天- JS 基础 JS 写在哪里 注释 行内 js 内部 js 外部 js JS 基础语法 语句 大小写 代码块 折行 变量 声明 var .let.const 及作用域 ...

  2. js进阶ajax函数封装(匿名函数作为参数传递)(封装函数引入文件的方式非常好用)...

    js进阶ajax函数封装(匿名函数作为参数传递)(封装函数引入文件的方式非常好用) 一.总结 2.匿名函数作为参数传递 二.js进阶ajax函数封装 ajax1.js 1 function ajax( ...

  3. 前端JavaScript(2) --常用内置对象,函数,伪数组 arguments,关于DOM的事件操作,DOM介绍...

    昨日内容回顾 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ...

  4. JS进阶之---函数,立即执行函数

    一.函数 函数声明.函数表达式.匿名函数 函数声明:使用function关键字声明一个函数,再指定一个函数名,叫函数声明.function name () { - } 函数表达式:使用function ...

  5. 前端提高篇(十一)JS进阶8函数参数及arguments

    形参与实参 基础点可以看这篇文章 获取形参个数:函数名.length function add(a,b,c,d,e){console.log('形参个数:' + add.length);} 运行效果: ...

  6. 自学JavaScript第四天- JS 进阶:AJAX Promise Canvas

    自学JavaScript第四天- JS 进阶:AJAX Promise Canvas AJAX 使用 XMLHttpRequest 使用 fetch() 方法 处理 AJAX 数据 安全限制 跨域方案 ...

  7. 5、JavaScript进阶篇②——函数、事件、内置对象

    一.函数 1. 什么是函数 函数的作用,可以写一次代码,然后反复地重用这个代码. 如:我们要完成多组数和的功能. var sum; sum = 3+2; alert(sum);sum=7+8 ; al ...

  8. JavaScript 面向对象编程(三) —— 函数进阶 / 严格模式 / 高阶函数 / 闭包 / 浅拷贝和深拷贝

    本篇为 JavaScript 进阶 ES6 系列笔记第三篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 系列笔记: JavaScript 面 ...

  9. JS进阶学习(作用域、函数进阶、解构赋值、原型链)

    文章目录 1.面相对象编程介绍 2.ES6中的类和对象 3.类的继承 ES6中的类和对象 三个注意点 作用域 局部作用域 全局作用域 作用域链 JS垃圾回收机制(GC) JS垃圾回收机制-算法说明 闭 ...

最新文章

  1. PyTorch 笔记(15)— 分别使用 tensor、autograd、torch.nn 搭建简易神经网络
  2. 干货 | 国家信息中心杜平谈关于数字化的几点思考
  3. PHP和js判断访问设备是否是微信浏览器实例
  4. spacevim 添加自动折行
  5. php opcache 坑,PHP7 opcache缓存清理问题
  6. linux日记的监控与分析,linux下apache日志监控与分析——webalizer与awstat
  7. c# winform程序调用托管dll(c#的dll),使用添加引用和动态加载dll
  8. iptables命令_程序员最有用的linux命令汇总
  9. H5 存储数据sessionStorage
  10. CCAI 2017 | 日本理化学研究所杉山将:弱监督机器学习的研究进展
  11. Android唤醒屏幕
  12. 安卓平台中的动态加载技术分析
  13. 获取单选按钮选中的值
  14. JVM07 - 方法区
  15. CSS、Bulma介绍
  16. centos安装五笔与拼音的办法
  17. SQL语言-更新操作命令
  18. Extracting Multiple-Relations in One-Pass with Pre-Trained Transformers [论文研读]
  19. Typora-PicGo-SMMS图床(Mac电脑和windows电脑)
  20. 抖音上热门原来这么简单-抖音培训-抖音上热门教程

热门文章

  1. 微信没有备份怎么恢复聊天记录?还能恢复吗?
  2. Ubuntu20.04安装Torque-6.1.2单机版(踩坑篇)
  3. Arduino UNO关于GRBL限位开关接线方式说明
  4. ilock计算机联锁系统应用,VPIILOCK型计算机联锁系统MMI操作手册V1.0.0
  5. 零售业数据分析应用的四种方式
  6. 持续集成交付的流水作业
  7. 太阳能自动灌溉系统 利用spwm实现逆变正弦波,仿真,程序,dxp原理图
  8. 自动生成12个月月份
  9. 边沿检测 Verilog
  10. 论文阅读笔记 | 目标检测算法——PP-YOLOv2