一、面向对象和基于对象
- 面向对象有三大特性:封装、继承、多态
- 基于对象:有封装的概念,但是实现不了真正的继承。通常是使用对象,但是无法利用现有的对象模板产生新的对象类型,继而产生新的对象,也就是说“基于对象”没有继承的特点。
- JavaScript是基于对象的语言,有三大特点:基于对象、多范式(多样化,没有统一的标准,比如创建对象和数组的方式)、动态性
面向对象:是先有一个抽象的对象描述(类),然后根据这个描述去构建新的对象(实例化对象),就好像盖房子,这个是根据图纸盖
基于对象:是先有一个具体的对象,然后根据这个具体的对象,再去创建新的对象(实例化对象),就好像盖房子,这个是根据盖好的房子的样子盖
原型链
- 原型:把所有可以共享的数据放在同一个对象中,再创建对象的时候,不会出现代码冗余,节省内存。(作用:实现同类对象之间的数据共享)
- 只要是函数,都会有原型。由此函数创建出来的对象,都能访问原型上的成员
- 获取原型:
- 通过函数:fnName.prototype
- 通过对象:obj.proto
- //获取对象的原型兼容处理
function getPrototype(obj){
return obj.__proto__ ? obj.__proto__ : obj.constructor.prototype;
}
原型特点:动态性、唯一性、不可变性、共享性
- 动态性:在已有的原型上添加成员,会反应到创建出来的对象上
如果置换了已有的原型,那么会反映到后来创建的对象上,对已经创建出来的对象没有影响 - 唯一性:由同一个构造函数创建出来的所有对象,都共享一个原型
- 不可变性:通过对象不能修改原型上的值(只能通过修改原型,来改变原型的属性值)
- 共享性:所有对象都可以直接访问原型上的成员
注意:在函数创建的对象上,给原型加方法,仍然可以共享
二、原型链

只有函数有原型prototype!
所有对象都有__proto__属性!
对象的 proto 属性指向创建该对象的函数的 prototype!
函数的 proto 属性指向 Function.prototype!
函数的原型的构造函数指向该函数!
函数的原型的__proto__属性指向Object.prototype!
Object.prototype的__proto__属性指向null!
//对象是由函数生成的,对象的__proto__属性是从生成它的函数的prototype那里得来的
var obj = {};//var o = Object();//Object函数
console.log(obj.__proto__ === Object.prototype )
//普通函数创建对象
var f = function(){};
var a = new f;
console.log(a.__proto__ === f.prototype )
//函数对象都是由Function函数生成的:
function fn(){}
console.log(fn.__proto__ === Function.prototype)
//Function函数本身作为对象时,生成它的函数是他自身!
console.log(Function.__proto__ === Function.prototype)
//Object函数既然是函数,那生成它的函数自然是Function函数
console.log(Object.__proto__ === Function.prototype)
//一般函数默认的prototype是一个类型为"object"的对象,
//它有两个属性:constructor和 __proto__。
//其中constructor属性指向这个函数自身,__proto__属性指向Object.prototype,这说明一般函数的prototype属性是由Object函数生成的。
function ff(){}
console.log(ff.prototype.constructor === ff)
console.log(Function.prototype.constructor === Function)
console.log(ff.prototype.__proto__ === Object.prototype)
//特殊情况:JavaScript原型链的终点
console.log(Object.prototype.__proto__ === null)
console.log(Function.prototype.__proto__ === Object.prototype)
/*
用instanceof,来判断__proto__走向
instanceof的作用:用于判断实例属于哪个构造函数。
instanceof的原理:判断实例对象的__proto__属性,和构造函数的prototype属性,是否为同一个引用(是否指向同一个地址)。

三、封装
封装就是方便的使用同一个方法,达到实现既定效果的目的。一般都会采用,将这个共同的方法写在原型中,实例指向的原型是一样的,不会消耗内存
(function() {
/***
* 信息提示组件Toast v1.0
* @param {Object} container 内容容器(必填)
* @param {Object} content 文字内容(可选)
* @param {Object} duration 显示时长(可选)
* 使用方法
* var toast = new Toast("toast", "你好,对话框");
* toast.show();(支持回调函数)
*/
function Toast(container, content, duration) {
this.container = document.getElementById(container);
this.content = content || "这是一段对话";
this.duration = duration || 2000;
}
Toast.prototype.show = function(callback) {
this.callback = callback || function() {};
this.container.style.opacity = 1;
this.container.style.display = "block";
this.container.innerHTML = this.content;
setTimeout(function() {
this.callback && this.callback();
this.hide();
}.bind(this), this.duration);
return this;
}
window.Toast = Toast;
})(window);
四、继承
https://blog.csdn.net/qq_34664239/article/details/83785441
五、闭包
优点:不产生全局变量,实现属性私有化。
缺点:闭包中的数据会常驻内存,在不用的时候要删掉否则会导致内存溢出。
function outFun() {
var num = 10;
return function() { // 内部的函数可以使用外部的函数 变量
console.log(num);
}
}
var obj = outFun();
// obj = function inFun() { console.log(num)}
obj();
//tab -- 闭包的节流
window.onload = function(){
//要想多个盒子不相互影响 ,我们可以通过id 给他们分开
//封装tab栏切换函数
function tab(obj){
var target = document.getElementById(obj);
var spans = target.getElementsByTagName("span");
var lis = target.getElementsByTagName("li");
for(var i=0;i<spans.length;i++)
{
// spans[i].index = i;
var timer = null;
spans[i].onmouseover = function (num) {
return function(){
clearTimeout(timer);
timer = setTimeout(function(){
for(var j=0; j<spans.length;j++)
{
spans[j].className = "";
lis[j].className = "";
}
spans[num].className = "current";
lis[num].className = "show";
},300)
}
}(i);
spans[i].onmouseout = function() {
clearTimeout(timer);
}
}
}
tab("one");
tab("two");
tab("three");
}
坑点及技巧
坑点1: 引用的变量可能发生变化
function outer() {
var result = []
for (var i = 0; i<10;i++){
result[i] = (function (num) {
return function() {
console.info(num);
// 此时访问的num,是上层函数执行环境的num,
//数组有10个函数对象,每个对象的执行环境下的number都不一样
}
})(i)
}
return result
}
坑点2: this指向问题
var obj = {
name: "object",
getName: function() {
return function() {
console.info(this.name)
}
}}
obj .getName()()
// underfined// 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows
可以用call方法改变this指向
obj .getName().call(obj)
坑点3:内存泄露问题
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
aler(el.id) //会导致闭包引用外层的el,当执行完showId后,el无法释放
}}
// 改成下面
function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
aler(id)
}
el = null // 主动释放el
}
技巧1: 用闭包解决递归调用问题
function factorial(num) {
if(num<= 1) {
return 1
} else {
return num * factorial(num-1)
}
}
console.log(factorial(4));
// 使用闭包实现递归
var newFactorial = (function f(num){
if(num<1) {return 1}
else {
return num* f(num-1)
}
})
//这样就没有问题了,实际上起作用的是闭包函数f,而不是外面的函数
技巧2:用闭包模仿块级作用域
for(var i=0;i<10; i++){
console.info(i)
}
alert(i) // 变量提升,弹出10
//为了避免i的提升可以这样做
(function () {
for(var i=0; i<10;i++){
console.info(i)
}
})()
alert(i)// underfined 因为i随着函数的退出,执行环境销毁,变量回收
1033

被折叠的 条评论
为什么被折叠?



