JavaScript高级与面向对象
对象:任何事物都可以看作是对象。
1、面向对象与面向过程的概念
- 面向过程:凡是自己亲力亲为,自己按部就班的解决现有问题。
- 面向对象:自己充当一个指挥者的角色,指挥更加专业的对象帮我解决问题。
- 联系:面向对象仍然离不开面向过程,可以认为它是对面向过程更高一层的封装。
2、创建对象的方式
字面量形式
var p = {};
p.name = '中国人';
p.age = '500';
构造函数形式 ==> 复用性更强
function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person('中国人', 500);
var p2 = new Person('中国人2', 500);
3、构造函数
概念
- 构造函数与普通函数本质上是一样的
- 编写构造函数时,首字母通常会大写,但不是必须的(类似变量驼峰命名法)
返回值特点
如果一个函数配合new关键字创建对象,那么这个函数也叫构造函数
- 如果构造函数没有return语句,那么new它,得到一个新实例
- 如果构造函数return了一些基本类型数据,那么new它,得到一个新实例
- 如果构造函数return了一个对象,那么new它,得到return的对象
4、类与实例的概念
类
- 比如动物类的定义是比较抽象的,它抽取了动物与动物之间的相同特征。
- 同样植物类、哺乳动物类、人类的定义也都是比较抽象的,也是提取他们的共同特征而形成的定义,这就是类。
- 在js中,可以把构造函数看作是类
实例
- 比如我家的旺财,是狗类的实例
- 我的弟弟妹妹,是人类的实例
- 在js中,通过构造函数创建的对象就是实例
类是对一些具有相同特征与特性事物的抽象描述
实实在在的具体事物就是某个类的实例。
联系:如果把类看作是模子,实例则是模子印出来的东西。
对象类型
- 比如数组是Array类型的对象,日期是Date类型的对象
- Array和Date就是其构造函数的名字
- 那么通过Person创建一个实例,那么这个实例就是Person类型的对象。
对象的类型就是其构造函数的名字
5、 原型
- 原型是一个对象,它的属性可以供其他对象共享
- js中有很多原型对象,基本每个对象都有属于自己的原型
- 原型对象的存在可以大大的节省内存开销
原型的使用
- 每个构造函数都有一个prototype属性,可以给其赋值
- 然后通过构造函数创建的实例就可以共享其属性与方法
6、面向对象与面向过程优缺点
面向对象
- 通常比面向过程消耗内存,因为有很多实例要存储
- 前期开发比较缓慢,但是复用性强,后期开发与维护进度会逐渐加快
- 变量的管理比较清晰,可读性较高
- 因为代码与对象间的职责比较清晰,所以后期可维护性和可扩展性也比较高
- 复用性更强
面向过程
- 变量混乱,可读性较差
- 通常有新需求出现,代码改动比较大,所以可维护性和可扩展性比较差
缺点
优点
缺点
优点:开发迅速,只要能解决当前问题即可
7、面向对象3大特征
- 封装性:对象可以把很多属性与方法集中在一起管理,就是js的封装性。
- 继承性:对象可以使用其原型对象的属性与方法,就是js的继承性。
- 多态性:js没有多态。如果非要说,那么对象形态、继承关系可以随时被改变,可以认为是js的多态性。
8、 面向对象的书写过程
- 根据需求提取解决该问题所需的对象
- 比如我要逛街,需要一个导购,需要一个保镖,需要一个女朋友
- 编写每一个对象所对应的构造函数
- 构造函数可以重复性创建实例,因为我可能需要多个保镖 function Person() {}
- 抽取对象所需的属性
- 就是该对象应该拥有的特征,比如人有名称、年龄、性别、四肢、双眼。 function Person(name) { this.name = name; }
- 抽取对象所需的方法
- 就是该对象应该拥有的特性,比如人会学习创造,狗会看门逗你笑 Person.prototype.study = function(){};
- 根据写好的构造函数创建实例,调用属性方法解决实际需求
- 就是调度实例干事 var p = new Person(); p.study();
9、 原型其他
谁有prototype与proto
- 每个函数都有prototype属性
- 每个对象都有proto属性
- 函数比较特殊,即是函数又是对象,所以prototype与proto都有
prototype与proto联系
- 通过构造函数创建的实例
- 当前构造函数的prototype属性指向谁,实例的proto属性就指向谁
如何得到一个对象继承的原型
- 通过proto属性(但是它是非标准属性,不建议开发中使用)
- 通过constructor属性得到对象的构造函数,再访问其prototype得到原型
创建对象时内在的4个步骤
- 创建一个新实例(本质上就是开辟了一块内存空间)
- 设置新对象的原型执行构造函数,执行时设置其this指向新实例
- 给新实例设置proto属性值
- 这个值与构造函数的prototype属性有关
- 赋值过程相当于这样:新实例.proto = 构造函数.prototype
- 返回新实例的地址
对象的属性访问规则
- 优先从自身查找
- 找不到就去原型找
- 还找不到继续去原型的原型找
- 直到终点,终点也没有返回undefined
对象的属性赋值
- 给一个对象的属性赋值
- 如果之前没有该属性那么就是新增,有就是修改
- 对象的属性赋值只影响自己,不会对其他对象和原型对象造成影响
10、原型常见书写方式
默认原型
function P() {}
P.prototype.fun = function(){};
var p = new P();置换原型
function P() {}
P.prototype = {
constructor: P,
fun: function(){}
};
var p = new P();extend复制扩展
function P() {}
extend(P.prototype, {}, {
fun: function(){}
}, {
fun2: function(){}
});
var p = new P();Object.create
var proObj = {
fun: function(){}
};
var p = Object.create(proObj);实现属性复制函数封装
function extend() {
var target = arguments[0];
for(var i = 1, len = arguments.length; i < len; i++) {
for(var key in arguments[i]) {
target[key] = arguments[i][key];
}
}
return target;
}类成员与实例成员
- 类成员(静态成员):添加给类自己的属性与方法
- 实例成员
- 添加给实例自己的属性与方法
- 原型上供实例使用的属性与方法
11、原型链
概念:一个对象继承的所有由proto属性串联在一起的对象,称为该对象的原型链。
对象原型链的研究方案
- 先通过proto得到对象的原型
- 然后访问这个原型的constructor属性,确定该原型的身份
- 然后继续按照上诉两个步骤,往上研究原型,最终就得到了对象的原型链。
规律与常见对象原型链结构
- 原型链的终点统一是Object.prototype
- 对象的原型和该对象的类型有关
- 比如Person的实例,原型是Person.prototype
- 比如Animal的实例,原型是Animal.prototype
- []的原型链结构 [] ==> Array.prototype ==> Object.prototype ==> null
- {}的原型链结构 {} ==> Object.prototype ==> null
- /abc/的原型链结构 /abc/ ==> RegExp.prototype ==> Object.prototype ==> null
- Person的原型链结构 Person ==> Function.prototype ==> Object.prototype ==> null
- Function的原型链结构 Function ==> Function.prototype ==> Object.prototype ==> null
- Object的原型链结构 Object ==> Function.prototype ==> Object.prototype ==> null
- 构造函数默认的prototype,它统一都继承Object.prototype
- 比如Person.prototype,原型是Object.prototype
- 比如Animal.prototype,原型是Object.prototype
- 通过这个规则,可以自由猜想出任意一个实例所有的原型
- 比如Book的实例,其原型结构为: Book实例 ==> Book.protoype ==> Object.prototype ==> null
12、运算符与方法
in -- 运算符
- 作用:判断能否使用某个属性(包含继承的属性)
- 语法:属性名 in 对象
- 返回值:boolean
hasOwnProperty -- 方法
- 作用:判断一个属性是不是自己的(不包含继承的属性)
- 语法:对象.hasOwnProperty(属性名)
- 返回值:boolean
关于for in遍历的补充:for in可以遍历对象继承的属性,不过一些内置的属性是不可遍历的。
delete -- 运算符
- 作用:删除对象的属性
- 语法:delete 对象.属性名 || delete 对象[属性名]
- 返回值:boolean
instanceof -- 运算符
- 作用:判断一个对象的原型链中是否含有某个构造函数的prototype
- 语法:对象 instanceof 构造函数
- 返回值:boolean
Function -- 内置构造函数
- 作用:创建函数实例
- 语法:new Function(形参1,形参2,...,代码体)
- 返回值:新创建的函数实例
- 特点:能够把字符串当做js脚本执行
eval -- 内置的全局函数
- 作用:执行字符串代码
- 语法:eval(字符串代码)
13、 函数四种调用模式
谨记:函数调用时,内部的this的值和这个函数定义无关,和运行(调用)有关。
函数调用模式 ==> 函数名() || 自调
这种方式调用,函数运行时内部的this指向全局对象window。
方法调用模式 ==> 对象.方法名() || 对象['方法名'] || 祖对象.父对象.子对象.方法名()
这种方式调用,函数运行时内部的this指向宿主对象。 (dom中事件绑定的函数,就是这种调用方式,所以this指向对应的dom对象)
构造函数调用模式 ==> new 构造函数() || new 对象.构造函数()
这种方式调用,函数运行时内部的this指向新创建的实例对象。
上下文调用模式(间接调用模式)
- 函数名.call(this, arg1, arg2);
- 函数名.apply(this, [arg1, arg2]); 这种方式调用,函数运行时内部的this指向call或apply传入的第一个参数; 如果没有传第一个参数,或者第一个参数为null、undefined,那么this统一指向window。
14、 作用域
概念:变量的有效范围。
全局变量
- 在全局都有效的变量。
- 定义方式:函数外定义。
- 生命周期:从定义开始,到页面被卸载结束。
局部变量
- 只在局部有效的变量。
- 定义方式:函数内定义。
- 生命周期:一般情况下,是从定义开始,到函数执行完毕结束。
函数作用域
- 只有函数才可以产生新的作用域
- 只有函数可以限定变量的有效范围
块级作用域 ==> js没有
- 凡是代码块就可以产生新的作用域
- 凡是代码块就可以限定变量的有效范围
词法作用域(静态作用域)
说的是变量的查找规则,特点是变量查找与函数定义有关,与调用无关
- 先在当前作用域查找
- 找不到就去定义该函数的作用域找
- 一直找到全局作用域为止,全局也没有则报错
作用域的产生:函数可以被多次重复调用,调用一次就会产生一个新的作用域。
作用域链
- 函数在定义的时候,将来它执行时的上级作用域就被确定好了,上级作用域可能还有上级,函数所有的上级作用域称之为作用域链。
- 一个函数作用域可以访问的所有上级作用域,称为它的作用域链。
垃圾回收机制原则
- 一个对象没有被其他变量或者属性引用,那么就会被释放。 同时还要保证该对象能够被使用,对于那些无法使用,又存在循环引用的对象,也会被释放。
- 一个局部变量没有被其他函数引用,那么就会被释放。
注意:有一个容易搞混,又没有什么联系的知识点,这里强调一下
- 函数内的this,与函数的定义无关,与调用有关。
function fn() {
console.log(a); // 报错,自己找不到,去定义fn的全局找,所以这里和fn的定义有关,与fn的调用无关。
}
(function() {
var a = 10;
fn();
})();
15、闭包
概念:在js中访问了自由变量的函数就是闭包
自由变量:函数可访问的外部局部变量,称之为该函数的自由变量
特点:闭包的自由变量生命周期会被拉长,与闭包的生命周期进行了捆绑
计数器案例
function getCounter() {
var total = 0;
return {
add: function() {
total++;
},
get: function() {
return total;
}
};
};
var counter = getCounter();
counter.add();
counter.get();
var counter2 = getCounter();
counter2.add();
counter2.get();缓存操作
var cache = (function() {
var cache = {};
return {
set: function(key, val) {
cache[key] = val;
},
get: function(key) {
return cache[key];
}
};
}());
cache.set('张锐', '中国人');
cache.get('张锐');for循环练习
var arr = ['第一句话', '第二句话', '第三句话'];
for(var i = 0, len = arr.length; i < len; i++) {
setTimeout(function(i) {
return function() {
console.log(arr[i]);
}
}(i), 1000 * i + 1000);
}
16、 预解析
- 可以理解为js解析引擎在逐行执行代码前,对一些特殊代码的预先执行。
- 预解析过后代码才会从上到下逐行执行,但是预解析时已经定义的变量与函数,是不会重复定义的。
- 预解析的本质就是变量对象初始化。
预解析做了两件事情
1、变量声明提升:检测到变量声明那就率先进行声明
2、函数声明提升:检测到函数声明也率先进行声明
变量声明
- 使用通过var定义的变量,才属于变量声明
var a; //属于变量声明。
b = 10;// 不属于变量声明。 - var关键字可以通过逗号连续声明多个变量
var a, b, c = 20, d = 30;
//a,b,c,d全部属于声明。 - var关键字在声明变量的时候,可以给其赋值,如果赋值表达式中含有一些变量,这些变量不属于变量声明。
var a = b = 10;
//其中a属于变量声明,b不属于。
函数声明
- 要么定义在全局
- 要么定义在另一个函数主体内
预解析的特点
在js中,函数声明式写法比较单一,好区分。
- 在变量声明之前访问它不会报错
- 在函数声明之前调用它不会报错
预解析相关细节
- js预解析分全局预解析与局部预解析,区别在于局部预解析在函数调用时发生。
- 变量声明重名 -- 最终只留一个
console.log(a); // 预解析后值保留一个变量a,值为undefined
var a = 1;
var a = 2; - 函数声明重名 -- 保留后面的函数
console.log(test); // 预解析后test为打印2的函数
function test(){ console.log(1) }
function test(){ console.log(2) } - 变量与函数重名 -- 保留函数
console.log(test); // 预解析后test值为函数
var test = 10;
function test(){}
var test = 20;
函数执行时形参会优先执行
形参定义与赋值优先于变量与函数声明。
(function(a) {
console.log(a); // a函数
var a = 200;
function a(){}
console.log(a); //
}(100));
函数表达式的名称
// 函数fnName的名字在外面无法访问,但是可以在函数内访问,
// 相当于自己的一个局部变量,值为自己的引用。
var fn = function fnName(){
console.log(fnName); // 里面可以访问
};
console.log(fnName); // 外面访问报错
17、 函数的四种调用模式
this的特点
- 函数中的this,调用方式不同,指向不同
- this与调用有关,与定义无关
1.17.1.1. 函数调用模式
函数名() || (function(){}()) ==> window
方法调用模式
对象.方法名() || 对象方法名 || 祖对象.父对象.子对象.方法名() ==> 宿主对象
构造器调用模式
new 构造函数() || new 对象.构造函数() ==> new出来的新实例
间接调用模式(上下文调用模式)
- call
- 函数.call(指定的this,实参1,实参2,...)
- 对象.方法.call(指定的this,实参1,实参2,...)
- apply
- 函数.apply(指定的this,[实参1,实参2,...])
- 函数.apply(指定的this,{0: 实参1, 1:实参2, length: 2})
- 对象.方法.apply(指定的this,[实参1,实参2,...])
- 异同
- call与apply都来自Function.prototype,所以所有的函数都可以使用。
- 都可以改变函数this的指向
- 不同之处在于传参的方式上
- 补充
- 函数调用call和apply,实际上是间接调用自己
- 例如fn.call(),表面上看是调用call方法
- 实际上连fn自己也被调用了,和fn()直接调用的区别是,this变了
call和apply方法借用的原理
- 如果一个方法内部操作的是this,那么我们就可以通过call或apply指定该方法this, this改变成谁,那么该方法最终操作的就是谁。
- 如果一个方法内部没有操作this,那么是无法借用的。
call和apply常见的使用场景
1.借用数组方法操作伪数组
// 给伪数组添加数据
var obj = {};
Array.protype.push.call(obj, '要添加的第一个值', '要添加的第二个值') // 通过伪数组获取对应的真数据(获取后原伪数组不会被改变,只是得到了新数组)
var argArr = [].slice.call(arguments);
2、借用Object.prototype.toString获取对象类型
var arr = [];
Object.prototype.toString.call(new Date).slice(8, -1)
3、借用父类构造函数给子类实例添加属性
function Parent(name, age) {
this.name = name;
this.age = age;
}
function Son() {
Parent.apply(this, arguments);
}
var p = new Son('火星人', 999);
// apply拆分数组或伪数组值依次传递给函数
var arr = [1, 10, 20, 40];
Math.max.apply(null, arr)
18、ES5数组新增的3个方法
forEach
- 作用:帮我们遍历数组,每遍历到一个值,就会调用一次回调,把这个值与它的下标传递过去
- 语法:数组.forEach(function(v, i){ console.log('使用forEach帮我们遍历好的值与下标') })
- 返回值:undefined
map
- 作用:可以用来代替forEach,但是map可以接收回调的返回值,最终通过一组数据映射为回调返回的另外一组数据
- 语法:var mapArr = 数组.map(function(v, i){ return v * v })
- 返回值:回调所有的返回值组成的新数组
filter
- 作用:可以用来代替forEach,但是还可以过滤数组中的值
- 语法:var filterArr = 数组.filter(function(v, i){ if(v % 2 ==0){ return true; } })
- 返回值:所有返回回调返回true的对应值组成的新数组
19、 call&apply的补充
- 如果不传参 ==> this指向window
- 传null ==> this指向window
- 传undefined ==> this指向window
- 传123 ==> this指向123的包装类型对象(Number对象)
- 传'abc' ==> this指向'abc'的包装类型对象(String对象)
- 传true ==> this指向true的包装类型对象(Boolean对象)
- 传对象 ==> this指向传入的对象
20、 严格模式
- ES5新增的一个特性,使用该特性可以让js以一种新的模式运行js脚本。
- 该模式下可以强制我们抛弃那些不推荐不友好的写法
- 该模式下可以让js之前的一些设计不太合理的api表现的合理一些
- 该模式下可以让js拥有一些新的特性,比如ES6/ES7规范中定义的某些语法,必须在严格模式下才有效
严格模式的分类
全局模式
- 在全局代码的最上面书写一句话'use strict';
- 使用该模式,所有的代码都按照严格模式执行
- 在函数内部的最上面书写一句话'use strict';
- 使用该模式,只有该函数内的代码才会按照严格模式执行
局部模式
需要记住的几条严格模式规则
- 定义变量必须使用var
- 函数调用模式this为undefined
- 真正实现了call谁this就为谁
其他
- 不能使用with语句
- 废除函数.caller与arguments.callee
- eval拥有了单独的作用域
JavaScript高级与面向对象的更多相关文章
- javascript高级特性(面向对象)
javascript高级特性(面向对象): * 面向对象: * 面向对象和面向过程的区别: * 面向对象:人就是对象,年龄\性别就是属性,出生\上学\结婚就是方法. * 面向过程:人出生.上学.工作. ...
- Javascript高级程序设计——面向对象小结
ECMAScript支持面向对象编程,对象可以在代码执行时创建,具有动态扩展性而非严格意义上的实体. 创建对象方法: 工厂模式:简单的函数创建引用类型 构造函数模式:可以创建自定义引用类型,可以想创建 ...
- Javascript高级程序设计——面向对象之理解对象
在面向对象语言中都有类的概念,通过类来创建具有属性和方法的对象.而ECMAScript中没有类的概念,ECMAScript中定义了对象:无需属性的集合,其属性值可以包含基本值.对象.或者函数. 在Ja ...
- JavaScript 高级之面向对象
1. 对象属性及方法 创建对象的方式 <script> //创建对象的方式一 var obj = {}; //创建对象的方式一 var obj = new Object(); </s ...
- 2020/06/06 JavaScript高级程序设计 面向对象的程序设计
ECMAScript虽然是一种面向对象的语言,但是他没有类的概念.所以他的对象也与其他语言中的对象有所不同. ECMA-262定义对象:一组没有特定顺序的值. 6.1 理解对象 创建对象的方法: 1. ...
- Javascript高级程序设计——面向对象之实现继承
原型链: 构造函数中都有一个prototype属性指针,这个指针指向原型对象,而创建的实例也有指向这个原型对象的指针__proto__.当实例查找方法时先在实例上找,找不到再通过__proto__到原 ...
- Javascript高级程序设计——面向对象之创建对象
对象创建方法: 工厂方法 构造函数模式 原型模式 组合构造函数和原型模式 寄生构造函数模式 问题构造函数模式 工厂模式: function Person(name, age){ var obj = n ...
- Javascript高级篇-面向对象的特性
一.创建对象 1.1初始化器 var any={ name:"some", age:10, action:function(){ alert(this.name+":&q ...
- javascript高级特性
01_javascript相关内容02_函数_Arguments对象03_函数_变量的作用域04_函数_特殊函数05_闭包_作用域链&闭包06_闭包_循环中的闭包07_对象_定义普通对象08_ ...
随机推荐
- 009-Shell 函数
一.函数定义 linux shell 可以用户定义函数,然后在shell脚本中可以随便调用. shell中函数的定义格式如下: [ function ] funname [()] { action; ...
- Uboot命令U_BOOT_CMD
转载:http://blog.csdn.net/shengzhadon/article/details/52766263 U_BOOT_CMD是一个宏定义,具体功能是定义一个struct cmd_tb ...
- Hadoop2.7.3 HA高可靠性集群搭建
1.背景介绍 Hadoop2.0.0之前,在一个HDFS集群中,NameNode存在单节点故障(SPOF):因为集群中只有一个NameNode,所以在使用过程中,如果该NameNode出现故障或数据丢 ...
- 记录:tensoflow改错TypeError: Cannot interpret feed_dict key as Tensor: Can not convert a float into a Te
错误描述: TypeError: Cannot interpret feed_dict key as Tensor: Can not convert a float into a Tensor. 改错 ...
- 关于/proc/进程idpid/fd ,根据fd来查找连接
当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽 ...
- 在vue中使用express-mock搭建mock服务
首先安装 nodemon ,如果是全局安装,那么所有的项目都可以使用mock服务 npm install nodemon 再安装express-mockjs npm i -D express-mock ...
- centos上安装redmine
1.下载bitnami的redmine安装包 https://bitnami.com/stack/redmine/installer 2.安装remine ./bitnami-redmine-3.3. ...
- Mysql 数据类型及选择原则
MySQL中的数据类型大的方面来分,可以分为:日期和时间.数值,以及字符串.下面就分开来进行总结. 数据库类型的选择对数据库的性能影响很大 1 . 数据类型会影响存储空间的开销 2 . 数据类型会影响 ...
- python3_time模块详解
python提供的时间模块time是需要单独引入: 1.time.sleep(secs)# 推迟调用线程的运行,secs指的是秒 time.sleep(secs) 2.time.time():返回当前 ...
- window连接linux共享
前提说明:windows主机信息:192.168.1.100 帐号:abc 密码:123 共享文件夹:sharelinux主机信息:192.168.1.200 帐号:def 密码:456 共享文件夹: ...