this相关
this指向
- 普通函数中this指向window
- 立即执行函数中this指向window
- 定时器中this指向window
- 构造函数中this指向创建的实例
- 对象中的函数的this指向当前的对象
- 事件函数中的this指向事件源(谁调用该事件就返回谁)
html代码:
<div class="box">点击</div>
js代码:
// 1.普通函数
function fn (){
console.log(this);
}
fn();// window
// 2.立即执行函数
(function(a){
console.log(this);
})(2); // window
// 3.事件函数
document.querySelector(".box").onclick=function(){
console.log(this); // 事件源 <div class="box">点击</div>
}
// 4.定时器
setTimeout(function(){
console.log(this); // window 这是一个异步函数
})
// 5.构造函数
function build (name,age){
this.name= name;
this.age = age;
console.log(this);
}
var pengerzi = new build("彭忠杰",18); // this指向实例 build {name: '彭忠杰', age: 18}
// 6.对象中的函数
var obj = {
name:"彭忠杰",
sex :"女",
func:function(){
console.log(this);
}
}
// 调用对象中的函数
obj.func(); // {name: '彭忠杰', sex: '女', func: ƒ} this指向当前对象
改变this指向(call、apply、bind)
- .call(需要修改的this指向,参数1,参数2,…)
- .apply(需要修改的this指向,[参数1,参数2,…]),可用于求最值
- bind(需要修改的this指向,参数1,参数2,…)
js代码
// call apply bind 改变this指向
var obj1 = {
x:2,
y:3
};
var obj2 = {
x:4,
y:5
};
var obj3 = {
x:1,
y:2
};
var x = 6,y=7;
function back (x,y) {
console.log(x,y);
console.log(this.x,this.y);
}
back(2,3); // 正常情况下this指向window, window.back(2,3) 2 3 ;67
// 使用call
back.call(obj1,8,9); // obj1不影响后面的参数 8 9 ; 23
// 使用apply
back.apply(obj2,[4,6]); // 使用数组形式传参 4 6 ; 4 5
// 使用bind
var bin = back.bind(obj3,8,9); // bind返回一个函数体,需要手动调用
console.log(bin); // 返回函数
bin(); // 8 9 ; 1 2
apply求最值
var arr = [6,5,9,4,2,3,4];
// 使用Math的min或者max
console.log(Math.max(6,5,9,4,2,3,4)); // 9
// 使用apply
console.log(Math.max.apply(Math,arr)); // 9
三个相互比较
- 相同之处:都可以改变this指向
- 不同之处:
- 传参方式不同:apply的参数以数组的形式传入;bind和call以枚举的形式传入
- 调用方式不同:call和apply是直接调用;bind需要手动调用
原型
- 成员:构造函数内部的属性
- 实例成员:构造函数内部通过this创建的成员,可以通过this打印出来
- 静态成员:后续添加的内容,可以访问,不能通过this获取到
- 显式原型:prototype。 给构造函数使用
- 隐式原型:__ proto__ 。 实例使用
function Per(name, age) {
// 实例成员,构造函数内部的成员
this.name = name;
this.age = age;
this.aa = function () { // 比较占内存 所以一般在原型上添加方法
console.log(11);
};
}
var zhangsan = new Per("张三", 15);
console.log(zhangsan);
// prototype 显式原型,只有构造函数使用显式原型
console.log(Per.prototype);
// __proto__ 隐式原型,实例使用隐式原型
console.log(zhangsan.__proto__);
console.log(Per.prototype === zhangsan.__proto__); // 构造函数的显式原型与实例的隐式原型是全等的
// 静态成员 后续添加的成员
zhangsan.fun = function () {
console.log("这里是张三");
}
console.log(zhangsan); // 能打印出fun
console.log(Per); // 打印不到fun
// 给原型添加方法
Per.prototype.fun2 = function () {
console.log("这是通过原型添加的方法");
}
console.log(Per.prototype); // 能打印出fun2
console.log(zhangsan.__proto__); // 能打印出fun2
var lisi = new Per("李四", 16);
console.log(lisi.aa);
console.log(zhangsan.aa);
console.log(lisi.aa === zhangsan.aa); // false 每次创建对象时,都会为aa这个方法创建一个空间,所以是比较浪费内存的,一般在原型上创建方法
原型链
万物皆对象,对象皆为空:所有的元素通过查找原型,最终的结果都是“null”;
构造函数实例=》构造函数原型=》Object原型=》null
function func(name, age) {
this.name = name;
this.age = age;
}
var pzj = new func("彭忠杰", 12);
console.log(pzj);
console.log(pzj.__proto__);
console.log(func.prototype);
console.log(pzj.__proto__ === func.prototype); // 实例的隐式原型与构造函数的显式原型全等
console.log("-----验证万物皆对象,对象皆为空---------------------------------");
// 验证万物皆对象,对象皆为空
// console.log(Object.prototype);
console.log(Object.prototype.__proto__); // null
// 构造函数
// console.log(func.prototype);
console.log(func.prototype.__proto__.__proto__); // null
// 实例
// console.log(ls.__proto__);
// console.log(ls.__proto__.__proto__);
console.log(ls.__proto__.__proto__.__proto__); // null
- constructor 用于指向原来的构造函数
构造函数可以通过prototype找到原型,原型可以通过constructor找到构造函数
// constructor的用处:指向原来的构造函数
func.prototype = {
fn:function(){ // 将原型进行覆盖
console.log(11);
},
}
var zs = new func("张三",13)
console.log(zs.__proto__); // 不能显示原来的原型
func.prototype = {
fn:function(){ // 将原型进行覆盖
console.log(11);
},
constructor:func, // 指向构造函数,后面再通过new创建的对象就可以正常显示原型了
}
var ls = new func("李四",15);
console.log(ls.__proto__); // 能正常显示原来的原型了
总结
- 构造函数的命名:首字母大写
- Date、String、Math、Array等都相当于构造函数,我们平时用的字符串、数组的方法都是封装在原型中的方法
- 构造函数有一个缺点,就是如果方法直接写在里面,每次创建新对象都会开辟一个新的空间,会占用内存,所以我们一般不直接在构造函数中添加方法,而是在原型中创建方法
- 万物皆对象,对象皆为空的意思是所有的元素都可以一层一层往上找,最终找到null
案例展示
我们通过给原型上添加方法,由其他元素进行调用,尝试做出一个Tab切换的效果。具体思路:创建构造函数,给构造函数的原型上添加方法,在构造函数内写好各种事件,最后通过new关键字,创建实例对象:
html代码:
<div id="bigBox">
<div class="btnBox">
<button class="color">按钮一</button>
<button>按钮二</button>
<button>按钮三</button>
</div>
<ul class="list">
<li class="block"></li>
<li></li>
<li></li>
</ul>
</div>
css代码:
* {
margin: 0;
padding: 0;
list-style: none;
}
.list>li {
width: 300px;
height: 300px;
display: none;
}
.list>li:nth-child(1) {
background-color: #f0f;
}
.list>li:nth-child(2) {
background-color: #0ff;
}
.list>li:nth-child(3) {
background-color: #ff0;
}
.list .block{
display: block;
}
#bigBox .color{
background-color: #ff4200;
color: #f0f00f;
border: 0;
}
js代码:
function Change(id) {
var oBox = document.getElementById(id);
this.btns = oBox.querySelectorAll("button");
this.lists = oBox.querySelectorAll("li");
// 绑定事件
for (i = 0; i < this.btns.length; i++) {
this.btns[i].onclick = this.qiehuan.bind(this,i);
}
}
Change.prototype.qiehuan = function (a) {
// 先清除所有的样式
for(var j = 0 ; j < this.lists.length ; j ++){
this.lists[j].className = "";
this.btns[j].className = '';
}
this.lists[a].className = "block";
this.btns[a].className = 'color';
}
var oClick = new Change("bigBox");