JavaScript (JS) 面向对象编程 浅析 (含对象、函数原型链、闭包解析)
1. 构造函数原型对象:prototype
① 构造函数独立创建对象,消耗性能
function Person(name) {
this.name = name;
this.sayHello = function () {
console.log("Hello,my name is " + this.name)
}
}
var P1 = new Person("Tom");
var P2 = new Person("Tom");
P1.sayHello();
P2.sayHello();
console.log(P1.sayHello == P2.sayHello) 返回值为false
返回值为false说明通过同一个构造函数独立创建的两个构造函数对象P1和P2的sayHello方法不相等,说明他们分别在内存中开辟了存储空间,消耗性能
② prototype:每一个函数都会有一个prototype属性
将同一个构造函数创建出的不同对象的不同函数方法,创建在该构造函数的prototype属性中,可实现只创建一次,所有该构造函数对象都可以调用这一方法

prototype是构造函数的原型属性,也是构造函数对象例如图中P1、P2的原型对象
③ prototype代码演示
(1)动态添加方法属性,方法是独立分布添加上的,保留prototype指向构造函数的constructor属性

function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("sayHello");
}
Person.prototype.study = function () {
console.log("study");
}
Person.prototype.goHome = function () {
console.log("goHome");
}
var P1 = new Person("Tom");
var P2 = new Person("Jim");
var P3 = new Person("Jack");
P1.sayHello();
P2.study();
P3.goHome();
(2)直接替换,创建一个对象添加方法属性,原本指向构造函数的constructor属性消失, 该替换添加在构造函数实例对象声明之前

function Person(name) {
this.name = name;
}
Person.prototype = {
constructor:Person, 手动添加constructor属性
sayHello: function () {
console.log("sayHello");
},
study: function () {
console.log("study");
},
goHome: function () {
console.log("goHome");
}
}
var P1 = new Person("Tom"); 声明构造函数对象
var P2 = new Person("Jim");
var P3 = new Person("Jack");
P1.sayHello();
P2.study();
P3.goHome();
④ __proto__:实例对象访问原型属性(双下划线) IE8以下不支持
构造函数通过prototype访问原型
实例对象通过__proto__访问原型
(1)示例代码如下:
var object = {};
object.name = "Tom";
object.age = 18;
object.__proto__ = {
hello: "hello",
color: "red"
}
console.log(object.hello);
通过这个方法,可以给实例对象统一添加方法属性
(2)兼容性处理 IE8兼容处理
利用构造函数,访问constructor的prototype,这样就可以访问原型
function Person(name) {
this.name = name;
}
function __getProto__(object) {
return object.constructor.prototype;
}
var ob = new Person("Jack");
console.log(__getProto__(ob) === ob.__proto__); 返回值为true
2. 继承
① HTML对象原型继承关系,示例div:

② 混入
通过给对象添加一个extend方法,动态给对象添加属性
arguments: 函数中默认的类似数组类型对象,里面存储了所有传入的参数
相关代码示例如下:
var object = {
extend: function (obj) {
for (var i = 0; i < arguments.length; i++) {
for (var k in arguments[i]) {
this[k] = arguments[i][k];
}
}
}
}
object.extend({name: "Tom"}, {age: 20, gender: "male"}, {
sayHello: function () {
console.log("Hello");
}
});
console.log(object)
③混合式继承 通过给构造函数的原型属性添加方法实现
示例代码如下:
function Person() {
Person.prototype.extend = function (obj) {
for (var i = 0; i < arguments.length; i++) {
for (var k in arguments[i]) {
this[k] = arguments[i][k];
}
}
}
}
var p = new Person();
p.extend({name: "Tom"}, {age: 20, gender: "male"}, {
sayHello: function () {
console.log("Hello");
}
});
console.log(p);
④ Object.create( ) 创建对象 IE8以下不支持该方法
示例代码如下:
var o = {name: "Tom", age: 20, gender: "male"};
var obj = Object.create(o);
console.log(obj);

创建的对象obj的属性和方法均继承自传入的对象o
Object.create( ) 方法的实现原理如下:
Object.create = function (obj) {
function F() { };
F.prototype = obj;
return new F();
}
IE8兼容性封装:
function createWithObject(obj) {
if (Object.create) {
return Object.create(obj);
} else {
function F() { };
F.prototype = obj;
return new F();
}
}
3. 对象的原型链 (构造函数)
示例代码如下:
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
};
function Student() { };
Student.prototype = new Person("Tom", 18, "male");
var student = new Student();
function Instance (){ }; 所有构造函数都指向相同的原型
以上代码所对应的原型链结构示意图如下:

4. Function创建函数
Function是一个构造函数 new Function得到一个函数
new Function(arg0,arg1,…,argN,body );最后一个参数body是函数执行的代码块(函数体),其他参数都是传入函数代码块的参数
代码示例如下:
var getSum = new Function(“num1” , ”num2” , ”return num1+num2”);
函数代码块、函数体中内容太多时书写方法:
1.直接书写: 高斯求和函数
var getSum = new Function(“min” , ”max” , ” var sum = 0;for (var i = min; i <= max; i++) { sum = sum + i;}return sum;”);
2.字符串拼接书写:
var getSum = new Function("min", "max",
"var sum = 0;" +
"for (var i = min; i <= max; i++) {" +
"sum = sum + i;" +
"}" +
"return sum;");
3.获取HTML中的value书写:
封装一个tool函数:
function tool(idName) {
var ele = document.getElementById(idName);
var code = ele.innerText;
ele.parentNode.removeChild(ele);
ele = null;
return code;
}
将需要传入的参数内容写入HTML中:
<div id="div">
var sum = 0;
for (var i = min; i <= max; i++) {
sum = sum + i;
}
return sum;
</div>
再创建函数:
var getSum = new Function("min", "max",tool("div"));
5.函数的原型链
在js中任何函数都是Function的实例
console.log(Function) 结果:function Function() { [native code] }
函数的基本原型链结构如下图所示:

6.完整的原型链结构图(对象与函数)

instanceof:判断构造函数的原型属性是否在该对象的原型链上
function Person() {};
var p = new Person();
console.log(p instanceof Object) 返回ture
console.log(p instanceof Person) 返回ture
console.log(Person instanceof Function) 返回ture
console.log(Person instanceof Object) 返回ture
不应该过多依赖原型链继承,非常消耗性能,解决办法如下:
var o = {
method: function () {
console.log("这是一个方法");
}
}
function Person() { };
Person.prototype = o;
function Student() {
this.method = Person.prototype.method; 通过这种方式快速访问method
}
Student.prototype = new Person(); 这样访问消耗性能,可以省略
var s = new Student();
s.method();
7.闭包 js中函数是具有作用域隔离特性的内存结果,可以作为一个闭包
①返回函数
function func() {
var num = Math.random();
function fn() {
console.log(num);
return num;
}
return fn;
}
var foo = func();
var f1 = foo();
var f2 = foo();
因为在func中返回了fn,所以func只会被调用一次,fn被调用两次,打印结果相同
②沙箱模式 一个隔离的执行环境
(1)递归函数性能优化,以斐波拉契数列为例:
var count1 = 0;
function fib1(n) {
count1++;
if (n == 1 || n == 2) {
return 1;
} else {
return fib1(n - 1) + fib1(n - 2);
}
}
console.log(fib1(30));
console.log(count1);
var count2 = 0;
var data = [1, 1]; 声明一个数组接收创建的fib(n)
function fib2(n) {
count2++;
if (data[n-1]) { 如果数组中有值,直接返回该值
return data[n-1];
} else {
return data[n-1] = arguments.callee(n - 1) + arguments.callee(n - 2);
}
}
console.log(fib2(30));
console.log(count2);

通过比较count1与count2的值可以发现,使用数组接收存储fib2(n)值的函数递归次数明显小于直接递归的fib1函数,性能得到极大优化
对斐波拉契数列函数进行闭包封装:封装一个自执行函数fib
var fib = (function () {
var data = [1, 1];
return function(n) {
if (data[n - 1]) {
return data[n - 1];
} else {
return data[n - 1] = arguments.callee(n - 1) + arguments.callee(n - 2);
}
}
})();
(2)闭包应用:封装一个带有事件添加和移除的函数
var tabbLoad = (function () {
var data = [];
window.onload = function () {
for (var i = 0; i < data.length; i++) {
data[i]();
}
}
return {
addEvent: function (fn) {
data.push(fn);
}, removeEvent: function (fn) {
for (var i = data.length - 1; i >= 0; i--) {
if (fn.toString() === data[i].toString()) {
data.splice(i, 1);
}
}
}
}
})();
tabbLoad.addEvent(function () {
console.log("执行函数1")
})
tabbLoad.addEvent(function () {
console.log("执行函数2")
})
tabbLoad.removeEvent(function () {
console.log("执行函数2")
})
(3)闭包应用:创建一个可以缓存的函数cache
var createCache = (function () {
var data = [], max = 3; max为设置的缓存最大值,在查看data数据时可以将var去掉,将data变为全局变量,但在实际应用时一定不能去掉
function cache(key, value) {
data.push(key);
cache[key] = value;
if (data.length > max) {
var temp = data.shift();
delete cache[temp];
}
}
return cache;
})();
cache("name1","Tom")
cache("name2","Jim")
cache("name3","Jack")
cache("name4","Lily")

JavaScript (JS) 面向对象编程 浅析 (含对象、函数原型链、闭包解析)的更多相关文章
- JS面向对象组件(一) ---包装对象与原型链
首先我们可以看看平时我们常用的 var str = 'hello'; alert(typeof str); //string var str = new String("hello" ...
- 04面向对象编程-01-创建对象 和 原型理解(prototype、__proto__)
1.JS中对象的"不同":原型概念 从Java中我们可以很好地去理解 "类" 和 "实例" 两个概念,可是在JavaScript中,这个概念 ...
- Js面向对象编程
Js面向对象编程 1. 什么是面向对象编程? 我也不说不清楚什么是面向对象,反正就那么回事吧. 编程有时候是一件很快乐的事,写一些小游戏,用编程的方式玩游戏等等 2. Js如何定义一个 ...
- 页面循环绑定(变量污染问题),js面向对象编程(对象属性增删改查),js字符串操作,js数组操作
页面循环绑定(变量污染问题) var lis = document.querySelectorAll(".ul li") for ( var i = 0 ; i < lis. ...
- JS面向对象编程,对象,属性,方法。
document.write('<script type="text/javascript" src="http://api.map.baidu.com/api?v ...
- JavaScript的面向对象编程(OOP)(一)——类
在学习JavaScript面向对象的编程之前,需要知道,并了解面向对象的一些基本的常识.初学者中大多数都以为面向对象中,面向对象的编程是很重要和占据很大一部分精力.笔者在之前也是认为OOP是面向对象的 ...
- JS面向对象编程(进阶理解)
JS 面向对象编程 如何创建JS对象 JSON语法声明对象(直接量声明对象) var obj = {}; 使用 Object 创建对象 var obj = new Object(); JS对象可以后期 ...
- js面向对象编程 ---- 系列教程
原 js面向对象编程:数据的缓存 原 js面向对象编程:如何检测对象类型 原 js面向对象编程:if中可以使用那些作为判断条件呢? 原 js面向对象编程:this到底代表什么?第二篇 原 js面向对象 ...
- 简单粗暴地理解js原型链–js面向对象编程
简单粗暴地理解js原型链–js面向对象编程 作者:茄果 链接:http://www.cnblogs.com/qieguo/archive/2016/05/03/5451626.html 原型链理解起来 ...
随机推荐
- 对mysql快速批量修改,查重
更新UPDATE mytable SET myfield = CASE id WHEN 1 THEN 'value' WHEN 2 THEN 'value' WHEN 3 THEN 'value' E ...
- Vue源码学习一 ———— Vue项目目录
Vue 目录结构 可以在 github 上通过这款 Chrome 插件 octotree 查看Vue的文件目录.也可以克隆到本地.. Vue 是如何规划目录的 scripts ------------ ...
- webpack 4.x 解决 webpack-dev-server工具在webpack构建的项目中使用问题
webpack-dev-server工具能实现自动打包编译和热更新 首先将webpack-dev-server安装到项目中 npm install webpack-dev-server -D 这时在命 ...
- 两种方法实现text输入框中“请输入关键字”的提醒
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 关于sql查询结果集的链接
开通博客有一段时间了,第一次博文.本身是个理工科的,没啥文采,就想着把平时遇到的问题记录下来,防止自己以后忘了还要去翻找. 今天看到同事写的代码,查询两张表里的数据,结果集类型是一样的.写了两条查询, ...
- nginx Keepalived高可用集群
一.Keepalived高可用 1.简介 Keepalived软件起初是专为LvS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能.因此, ...
- CentOS6 x86_64最小化安装优化脚本
#!/bin/bash #centos6. x86_64系统最小化安装优化脚本 #系统基础优化,建议以root运行 if [ $USER != "root" ];then echo ...
- lnmp启用pathinfo并隐藏index.php
编辑如下区段: location ~ [^/]\.php(/|$) { # comment try_files $uri =404; to enable pathinfo try_files $uri ...
- DiyCode开源项目 TopicActivity 分析
1.首先看看TopActivity效果. 2.TopicActivity是一个继承BaseActivity的.前面分析过BaseActivity了.主要有一个标题栏,有返回的图标. 3.贴一下T ...
- easyui-numberbox限定仅输入数字
许多必填项都涉及到数字,比如电话号码,身份证号这些要求用户在输入时只能输入数字.Easyui提供了数字框控件,允许用户只输入数字, <td> <input id="ssd& ...