面向对象学习记录,构造函数,原型导读,ES5方法,闭包,递归。

构造函数和原型导读

三种创建对象方式

  1. 利用new Object()创建
	var obj = new Object();
  1. 利用对象字面量创建
	var obj = {}
  1. 利用构造函数创建对象
	function Star(name,age){
	this.name = name;
	this.age = age;
	this.sing= funcrion(){
		console.log("我会唱歌")
		}
	}
	var wanwan = new Star('wanwan',18)

实例成员和静态成员

  • 实例成员就是构造函数内部通过shis添加的成员,实例成员只能通过实例化对象来访问
	function Star(name.age){
		this.name = name;
		this.age = age;
		this.sing = function(){
			console.log("ok")
		}
	}
	var obj = new Star("wanwan",18);

构造函数原型对象

  • prototype原型对象和__proto__对象原型
function Star(name,age){
	this.name = name;	
	this.age = age;
	this.sing = function(){
		console.log('ok')
	}
}
var a = new Star('aoao',18);
var w = new Star('wanwan',18);
console.log(a === w)//结果为false,两者不存在同一空间
Star.portotype.sing = function(){
	console.log('ok')
}
console.log(a === w)//结果为true
//每个对象都会有一个__proto__指向prototype
//__proto__对象原型和prototype是等价的
//__proto__对象原型的意义是为对象的查找机制提供一个方向,它是一个非标准属性,不可以赋值,只是指向原型对象prototype
console.log(a.__proto__ === prototype)//true

原型constructor

function Star(name,age){
 this.name = name; 
 this.age = age;
 this.sing = function(){
  console.log('ok')
 }
}
var a = new Star('aoao',18);
var w = new Star('wanwan',18);

User.prototype = {
//此时的原型对象被修改赋值的是一个对象,则必须手动利用constructor指回原来的构造函数
	constructor: User,
	lovely:function(){
		console.log('lovely')
	},
	sing:function(){
		console.log('sing')
	}
}
console.log(User.prototype.constructor)//Object
console.log(w.__proto__.constructor)//Object

构造函数,实例,原型对象三者之间的关系

里插入图片描述

原型链

如果都有相同属性,执行就近原则,如果都没有返回undefined
在这里插入图片描述

<script>
	function User(name,age){
 		this.neme = name;
 		this.age = age;
 	}
 	        // User.prototype.sex = '女2'
 	           Object.prototype.sex = '女3'
 	           var ww = new User("wanwan",18);
 	           // ww.sex = '女1' 
 	           console.log(ww.sex);
</script>
//如果都有sex属性,那么执行就近原则。如果都没有则返回undefined

原型对象this指向

不管是构造函数还是原型对象里的this都是指向实例对象

<script>
function User(name,age){
        this.name = name;
        this.age = age;
    }
var that;
User.prototype.sing = function(){
        console.log('会唱歌');
        that = this;
 } 
var ww = new User('wanwan',19);
ww.sing();        
console.log(that === ww); //true
</script>

利用原型对象扩展内置对象方法

Array.prototype.sum = function(){
	var sum = 0;
	for(var i=0;i<this.length;i++){
		sum += this[i];
	}
	return sum;
}
//此方法不可用会覆盖原有的方法,会报错!
//Array.prototype.sum = {
//         sum:function(){
//         var sum = 0;
//         for(var i=0;i<this.length;i++){
//         sum += this[i];
//	}                
//  	return sum;
//	}
// }

var arr = [1,2,3];
soleon.og(arr.sum())//6
//打印内置对象方法,此时内部新添加了自定义sum()方法
console.log(Array.prototype)

call()方法的作用

function demo(x,y){
       console.log('奥里给');
       console.log(this);
        console.log(x + y);
}
var a = {
            name: 'xixi'
      }
//1.call()可以调用函数
 // deme.call()
 //2.call()可以改变这个函数的this指向
 //此时打印this的指向是a这个对象
 demo.call(a,6,6)
 //后面可以象demo传参

利用父类构造方法继承属性

function Father(name.age){
	this.name = name;
	this.age = age;
}
function Son(name,age){	
	//改变this指向,这里的this指向了子类,继承了父类构造函数的属性
	Father.call(this,name,age);
	this.sex = '男';
}
var son = new Son("xixi",18);
sonsole.log(son)

利用原型对象继承方法

function Father(name,age){ 
         this.name = name;
         this.age = age;
 }
 //定义父类原型对象
Father.prototype.money = function(){
       console.log('工作');
 }
function Son(name,age){
              Father.call(this,name,age);
                      }
//利用原型对象继承父类方法
// 1.不可选,这样是同一对象,子类会修改公共的原型对象
// Son.prototype = Father.prototype;
//2.        Son.prototype = new Father();
//如果利用了对象的形式修改原型对象,一定要用cunstructor指回原来的原型对象
Son.prototype.cunstructor = Son;
//定义子类原型对象
Son.prototype.exem = function(){
            console.log('上学');
}
var son = new Son('yaya',18);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.cunstructor);
 </script>

函数进阶导读

函数的定义和调用

  • ES5之前通过构造函数 + 原型实现面向对象编程
    (1)构造函数有原型对象prototype
    (2)构造函数有原型对象prototype里面有constructor指向构造函数本身
    (3)构造函数可以通过原型对象添加方法
    (4)构造函数创建的实例对象有__proto__原型指向构造函数的原型对象

ES5通过类实现面向对象编程

class Star{}
console.log(typeof Star);//function
//1.类的本质其实还是一个函数,也可以理解为函数的另一种写法
//(1)类有原型对象prototype
console.log(Star.prototype);
//(2)类的原型对象prototype里有cunstructor指向本身
console.log(Star.prototype.constructor);
//(3)类可以通过原型对象添加方法
Star.prototype.test = function(){
        console.log('ok');
 }
var demo = new Star();
console.log(demo);
//(4)类创建的实例对象有__proto__原型指向类的原型对象
console.log(demo.__proto__ === Star.prototype);//true

ES5数组方法

遍历方法: forEach(),mao(),filter(),some(),every()

  1. forEach(),用法数组.forEach(数组当前项的值,数组当前值的索引,数组对象)
var arr = [1,2,3];
var sum = 0;
arr.forEach(function(value,indx,array){
    console.log('元素是'+value);
    console.log('索引是'+indx);
    console.log('数组本身'+array);
    sum += value;
})   
console.log(sum);
  1. filter()方法创建一个新的数组,主要用于筛选数组,
    注意它直接返回一个新数组
var arr = [10,20,30,40];
//返回的是一个新数组需要变量接收数组
var newArr = arr.forEach(function(value,indx,array){
      return value >= 20; 
 })
console.log(newArr);
  1. some()方法用于检测数组中是否有满足条件的元素
    注意它返回的是布尔类型,有就返回true无就返回false,如果第一个条件满足就终止循环不在继续查找
var arr = [1,'a',2,'b',3,'c'];
var flag = arr.some(function(value){
	return value == 1;
})
console.log(flag)//ruue

字符串方法

  1. trim():去除字符串两侧空格
var str = '  aa  bb  ';
console.log(str.trim());
//打印aa  bb

对象方法

  1. Object.defineProperty(obj,prop,descriptor),定义对象中的新属性或修改原有的属性.
  • obj:必须,目标对象
  • prop:必须,需定义或修改的属性名
  • descriptor:必须,目标属性所有的特性
var str = {id:1,name:‘mini’,price:1998};
//1原来修改和添加方法
str.num = 1000;
str.price = 998;

//2.Object.defineProperty()方法
Object.defineProperty(str,'num',{
	//value:设置属性的值,默认为undefined
	value:998,
});
Object.defineProperty(str,'id',{
	//writable:值是否可以重写,true | false,默认为false无法修改
	weitable:false,
});
Object.defineProperty(str,'color',{
	value:'skyblue',
	//enumerable:如果为false则不允许遍历,默认false
	enumerable:false,
	//cnofigurable:如果为false则不允许删除这个属性,不允许在线修改第三个参数里的特性,默认false
	cnofigurable:false,
	//如果修改会报错
})

函数的定义与调用

  1. 自定义函数(命名函数)
function fn1(){}
fn1();
window.fn1();//指向window
  1. 函数表达式(匿名函数)
var f = function(){};//this指向f
  1. 利用new Function(‘参数1’,‘参数2’,‘函数体’)
var f = new Function('a','b','a + b ')
f(6,6);//this指向f
  1. 绑定事件函数
btn.onclick = function(){
	console.log(this);//this指向绑定元素
}
  1. 定时器函数
window.setTimeout
setTimeout(function(){
	console.log(this)//指向window
},2000)
  1. 立即执行函数
(function(){
	console.log(this)//指向window
})()

改变this指向方法

  1. call()方法,可以改变this指向,一般用于继承
function Father(name,age){
	this.name = name;
	this.age = age;
}
function Son(name,age){
 	//这个this是用call方法把Son的this指向Father里的this,从而实现继承
	Father.call(this,name,age)
}
var son = new Son('wanwan',18);
console.log(son);
  1. apply()也是调用函数,但是它的参数必须是数组(伪数组),一般常用借助数学内置对象求最大最小值
var ww = {name:  'wanwan'}
function fn(arr){
   console.log(this);
   console.log(arr);
};
fn.apply(ww,['skyblue']);
//应用例如
var arr = [1,3,5,7,9];
//var max = Math.max.apply(null,arr);
//这里null要写为Math指向Math;
var max = Math.max.apply(null,arr);
console.log(max)//9
  1. bind()方法改变this指向但是不立即调用,返回新的函数
<button>按钮</button>
<button>按钮</button>
<button>按钮</button>
<script>
var ww = {name: "wanwan"}
function fn(x,y){
      console.log(this);
      console.log(x + y); 
};
var f = fn.bind(ww,6,9);
f();
//常用例如:点击按钮三秒后才能再次点击
var btn = document.querySelector('button');
//单个按钮
btn.onclick = function(){
	this.disabled = true;
	setTimeout(function(){
	this.disabled = false;
    }.bind(this),3000)
}
//多个按钮
var btns = document.querySelectorAll('button');
for(var i=0;i<btn.length;i++){
           btns[i].onclick = function(){
           this.disabled = true;
           setTimeout(function(){
           this.disabled = false;}.bind(this),2000)
      }
}

call apply bind

相同点:

  1. 都可以改变this指向

区别:

  1. call和apply会调用函数并改变函数内部this指向
  2. call和apply传递的参数不一样,call传递aru1,aru2…形式,app必须以数组形式[‘arg’]
  3. bind不会调用函数,可以改变this指向

主要应用场景

  1. call经常做继承
  2. apply经常跟数组有关,比如借助数学对象实现数组最大最小值
  3. bind 比如改变定时器内部的this指向

严格模式

全局严格模式

<script>
   //开启全局严格模式
   'use strict';
</script

开启函数内部严格模式

 function fn(){
  'use strict';
};

我们的变量名必须先声明再使用

num = 10;
console.log(num);//报错:num is not defined
var num = 10;
console.log(num);

我们不可以删除已经定义好的变量

var num = 10;
delete num;
//报错:Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.

严格模式下的this指向问题

  1. 以前在全局作用域函数中的this指向window对象
  2. 严格模式下全局作用域中函数的this是undefined
  3. 以前构造函数时不加new也可以调用当普通函数,this指向全局
  4. 严格模式下如果构造函数不加new调用this会报错
  5. new 实例化的构造函数指向创建的对象实例
  6. 定时器this还是指向window
  7. 事件,对象还是指向调用者

严格模式下函数变化

1.函数不能有重名的参数
2.函数必须声明在顶层,不允许在非函数的代码块内声明函数,例如if,for内部不允许,函数内可以声明函数

高阶函数

==高阶函数是对其他函数进行操作的函数,它接收函数最为参数或将函数作为返回值输出

闭包

闭包值指有权访问另一个函数作用域中变量的函数,就是哟个作用域可以访问另一个函数内部的局部变量。

闭包的作用

  1. 主要延伸了变量的作用范围
第一种:
function fn1() {
	//1.声明变量
	var sum = 10;
	function fn2() {
	//3.访问了另外一个函数的变量产生了闭包
		console.log(num)
	};
	//2.调用fn2函数
	fn2();
};
fn1()//打印10
第二种:
function fn3() {	
	//1.声明变量
	var num = 10;
	function fn4() {
		//3.调用了局部变量的num
		console.log(num);
	}
	//2.返回一个fn4函数
	return fn4;
};
var f = fn3();
//因为内部return了函数fn4,类似于一下操作
var f = function fn4() { console.log(num)}
简化版:
function fn3() {
	var num = 10;
	return function(){
	//就是高阶函数,闭包也是高阶函数的一种用法
		console.log(sum)
	}
}

闭包的应用

1.利用闭包方式得到所有li的索引

<ul>
	<li>第一个li</li>
	<li>第二个li</li>
	<li>第三个li</li>
</ul>
<script>
var lis =  document.querySelector('ul').querySelector('li');
//第一种:利用动态添加属性的方式
for(var i=0;i<lis.length;i++) {
	//需要为每个li动态的添加一个属性
	lis[i].index = i;
	//为每个li添加点击事件,并打印出当前li的索引号
	lis[i].onclick = function(){
		//此时的打印的索引都是3,因为是异步
		console.log(this.index)
	}
}
第二种:利用闭包
for(var i=0;i<lis.length;i++) {
//立即执行函数也称为小闭包,使用了外部的i
	//2.再把接收的i传给下面的i
	(function(i){
		lis[i].onclick = function() {
			//3.打印当前的i,就是当前的索引号了
			console.log(i)
		}
	})(i)//1.这里的i接收for里的i
}
</script>
  1. 定时器中的闭包
<ul>
 	<li>第一个li</li>
 	<li>第二个li</li>
 	<li>第三个li</li>
</ul>
<script>
var lis =  document.querySelector('ul').querySelector('li');
//利用闭包3秒后打印三个li
for(var i=0;i<lis.length;i++) {
	(function(i){
		setTimeout(function(){
			console.log(lis[i].innerHTML)
		},3000)
	})(i);
}
</script>

闭包练习

  • 闭包应用计算车费,起步价10(3公里内),没3公里加5,如果堵车要加5快.
var car = (function(){
	var start = 10;//起步价
	var total = 0;//总共车费
	return {
		//正常车费
		price: function(p){
			//如果在起步价以内
			if( p <= 3){
				total = start;
			}else{
			//如果大于3公里
				total = statr + (p - 3)*5
			}
			return total;
	},
		//堵车费用
		jam: function(flag){
				//如果堵车就加5,否则就直接返回
				return flag ? total + 5 :total
			}
}
})();
//打印5公里和堵车费用
console.log(car.price(5),car.jam(true))

递归

递归函数:函数内部自己调用自己,这个函数就是递归函数

var num = 1;
function fn() {
	if(num == 3){
	return;//递归必须添加退出条件
	}
   fu();
};
fn();
  1. 利用递归计算阶乘
function fn(n){
	if(n == 1){
   	return 1;
   }
	return n + (n-1);
	fn();
};
console.log(fn(3))//返回6
//解析:
return 3 * (fn(3-1))
return 3 * (fn(2))
return 3 * (2 * fn(2-1))
return 3 * (2 * fn(1))
return 3 * (2 * 1)
  1. 利用递归遍历数组
var data = [{
		id: 1,
		name: '零食',
		{
		id: 2,
		name: '可乐',
		waters:[{
			id: 22,
			name: '可口可乐'
		},{
			id: 23,
			name: '百事可乐'
		}]
}];
//利用递归获取用户输入数子的返回相应结果
function getId(data,id){
	//用forEach遍历里面的每一个对象
	data.forEach(function(item){
		//可以声明一个变量保存数据
		var o = {};
		//如果用户输入的是外层就输出外层
		if(item.id == id){
			//console.log(item);
			o = item;
		}else if(item.foods && item.foods.length > 0){
		//如果外层找不到再把item内部foods传进去继续找
			o = getIid(item.foods,id);
		}else if(item.waters && item.waters.length > 0){
			o = getId(item.waters,id);
		}
	});
	//遍历完了之后打印o
	return o;
};
console.log(getId(data,22))

浅拷贝和深拷贝

浅拷贝

浅拷贝只是拷贝第一层,更深层次对象级别只能拷贝引用

var obj = {
	id: 1,
	name: 'wanwan',
	msg: {
		age: 18
	}
};
var w = {};
//把obj拷贝给w
for(var k in obj) {
//k 是属性名  obj[k]  是属性值
	w[k] = obj[k];	
}
w.msg.age = 19;
//浅拷贝内容指向的是同一地址,数据改变都会改变
//浅拷贝提供的方法,把obj拷贝给w
Object.assign(w,obj)

深拷贝

深拷贝拷贝多层,每一级的数据都会拷贝

var obj = {
	 id: 1,
	 name: 'wanwan',
	 msg: {
 		 age: 18
	 },
	 color:['skyblue','pink']
};
var w = {};
function deppCopy(newobj,oldobj) {
	for(var k in oldobj) {
		//先获取属性值才可以判断
		var item = oldobj[k];
		//判断属于什么数据类型再拷贝
		if(item instanceof Array) {
			newobj[k] = [];
			deppCopy(newobj[k],item)
		}else if(item instanceof Object) {
			newobj[k] = {};
			deppCopy(newobj[k],item)
		}else{
			newobj[k] = w;
		}
	}
}
deppCopy(o,obj)
console.log(o)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值