构造函数
new关键字的执行过程:
在内存中创建一个空对象
this指向这个空
给空对象添加属性和方法
构造函数不需要return
静态成员和实例成员
构造函数里面的属性和方法 我们称为成员
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌');
}
}
实例成员 就是构造函数内部 通过this指向的成员,上述代码中,uname、age、sing就是实例成员
var dlrb = new Star("迪丽热巴", 18);
console.log(dlrb.uname);
console.log(Star.uname);
实例成员只能通过实例化对象来访问
Star.address = "哈尔滨";
console.log(dlrb.address);
console.log(Star.address);
静态成员 在构造函数上添加的成员
静态成员 只能通过构造函数来访问
构造函数的问题
function Star(uname, age) {
this.uname = uname;
this.age = age;
//问题就是多个对象的方法会占用好多内存,浪费内存
// this.sing = function () {
// console.log('我会唱歌');
// }
}
//每一个构造函数都有一个属性 指向原型对象 prototype
console.dir(Star)
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh, zxy);
ldh.sing();
console.log(ldh.sing === zxy.sing);
一般情况,我们的公共属性定义到构造函数里面
公共的方法放到原型对象上
对象原型__proto__
var obj = {};
console.dir(obj)
// var arr = [];
// console.log(arr);
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var dlrb = new Star("迪丽热巴", 18);
// 对象身上系统自己添加了一个__proto__
// 构造函数有一个属性叫prototype
// 对象的__proto__ 指向 构造函数的prototype
console.log(dlrb);
console.log(dlrb.__proto__ === Star.prototype);
//查找规则
//首先看自己对象本身有没有这个方法 有的话 执行自己的
//如果没有 就去原型对象查找 找到就可以执行
原型
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// Star.prototype.sing = function () {
// console.log("我会唱歌");
// }
// Star.prototype.eat = function () {
// console.log("我会吃");
// }
Star.prototype = {
// 如果我们修改了原来的原型对象,给原型对象重新赋值 一个新的对象,导致原来的原型对象的constructor属性消失
//解决办法:手动添加一个constructor 属性
sing: function () {
console.log("我会唱歌");
},
eat: function () {
console.log("我会吃");
},
// constructor 是原型对象的属性
constructor: Star
}
var fdd = new Star();
console.log(Star.prototype);
// 构造函数的 prototype对象默认都有一个 constructor属性,指向 prototype 对象所在函数。
console.log(Star.prototype.constructor);
console.log(fdd.__proto__.constructor);
原型链
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
// 只要是对象 就有一个属性 __proto__ 原型 , 指向原型对象
console.log(Star.prototype);
// Star.prototype 原型对象 本质也是一个对象 所以有__proto__ 原型, 指向Object原型对象
console.log(Star.prototype.__proto__);
console.log(Star.prototype.__proto__ === Object.prototype);
// Object原型对象 本质也是一个对象 所以有__proto__ 原型 指向null
console.log(Object.prototype.__proto__);
原型对象的this指向
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var that;
Star.prototype.sing = function () {
console.log("唱歌");
console.log(this);
that = this
}
// 1. 在构造函数中,里面this指向的是对象实例 ldh
var ldh = new Star('刘德华', 18);
// 2、在原型对象函数里 this也指向实例化对象 ldh
ldh.sing();
console.log(that === ldh);
给内置对象新增方法
//给原型对象扩展这个求和的方法
Array.prototype.sum = function () {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i]
}
return sum;
}
var arr = [1, 2, 3];
console.log(arr.sum());
console.log(Array.prototype);
call方法
function fn(a, b) {
console.log("今天星期一");
console.log(this);
console.log(a + b);
}
// fn();
var obj = {
uname: "fanfan"
}
// 1、调用函数
// fn.call();
//2、改变this指向 第一个参数 就是this的指向 剩下的参数就是正常传参数
fn.call(obj, 1, 2)
继承
// 父构造函数
function Father(uname, age) {
// this 指向父构造函数实例的对象
this.uname = uname;
this.age = age;
}
//子构造函数
function Son(uname, age) {
// this 指向子构造函数实例的对象
Father.call(this, uname, age)
}
var son = new Son("xiaoxiao", 18)
console.log(son);
借用原型对象继承方法
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function () {
console.log(1000000000);
}
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score;
}
//这样直接赋值会有问题 改了子构造函数原型上的方法 父原型对象也会跟着修改
// Son.prototype = Father.prototype;
Son.prototype = new Father();
//如果利用对象的形式修改原型对象,千万别忘了改这个constructor属性 指回原来的构造函数
Son.prototype.constructor = Son;
Son.prototype.study = function () {
console.log("儿子就的学习");
}
console.log(Father.prototype);
console.log(Son.prototype.constructor);
函数的定义和调用方式
//1、自定义函数(命名函数)
function fun() { }
fun();
//2、函数表达式(匿名函数)
var fn = function () { }
fn();
//3、利用new Function("参数1","参数2"...,函数体)
var f = new Function("a", "b", "console.log(a+b)");
f(1, 2);
// 所有的函数 都是 Function的实例对象
console.dir(f)
// instanceof
// 函数也属于对象
console.log(f instanceof Object);
函数的调用方法
// 1. 普通函数
function fn() {
console.log('人生的巅峰');
}
fn();
fn.call()
// 2. 对象的方法
var o = {
sayHi: function () {
console.log('人生的巅峰');
}
}
o.sayHi();
// 3. 构造函数
function Star() { };
new Star();
//4、绑定事件函数
btn.onclick = function () { };//点击按钮调用函数
//5、定时器
setInterval(function () { }, 1000);//每隔1秒调用这个函数
//6、立即执行函数
(function () { })() //立即执行函数 自动调用
this的指向
// 函数的不同调用方式决定了this 的指向不同
// 1. 普通函数 this 指向window
function fn() {
console.log('普通函数的this' + this);
}
fn()
// 2. 对象的方法 this指向的是对象 o
var o = {
sayHi: function () {
console.log('对象方法的this:' + this);
}
}
o.sayHi();
// 3. 构造函数 this 指向 实例对象
function Star() { };
Star.prototype.sing = function () {
console.log(this);
}
var ldh = new Star();
//4、绑定事件函数 this 指向 事件源
var btn = document.querySelector('button');
btn.onclick = function () {
console.log('绑定时间函数的this:' + this);
};
// 5. 定时器函数 this 指向的也是window
window.setTimeout(function () {
console.log('定时器的this:' + this);
}, 1000);
// 6. 立即执行函数 this还是指向window
(function () {
console.log('立即执行函数的this' + this);
})();
apply方法
//apply 方法 也可以改变this的指向
function fn(arr) {
console.log(this);
console.log(arr);//"fanfan"
}
// fn();
var obj = {
name: "fanfan"
}
var arr = [1, 2]
fn.apply(obj, arr)
fn.apply(obj, arr)
//1、apply可以调用函数 也可以变this的指向
//2、它的参数必须是数组(伪数组)
//3、apply的主要应用 比如 我们可以利用apply借助于数学内置对象求数组的最大值 最小值
var arr = [1, 2, 3, 3, 4];
var max = Math.max.apply(Math, arr);
console.log(max);
//apply参数传递规则
// function foo() {
// var str = Array.prototype.join.apply(arguments, ["-"]);
// return str;
// }
// var str = foo(1, 2, 3, 4, 4)
// console.log(str);
var str = ""
function fn(arr) {
console.log(this);
str += arr;
return str;
}
// fn();
//apply 方法在调用的时候 会将数组里面每一个元素拿出来,作为形参,挨个传递给函数
var obj = {
name: "fanfan"
}
var arr = [1, 2]
var res = fn.apply(obj, arr)
console.log(res);
bind方法
/bind方法 改变this的指向 不会立即执行
var obj = {
name: "fanfan"
}
function fn() {
console.log(this);
}
var fun = fn.bind(obj);
fun();
//1、不会调用原来的函数 可以改变原来函数this的指向
//2、返回的是原函数改变this之后的新函数
//3、什么时候用bind 想改变this指向 但是我不想立即执行原来的函数
// var btn = document.querySelector("button");
// btn.addEventListener("click", function () {
// this.disabled = true;//this指向btn
// var that = this;
// setTimeout(function () {
// that.disabled = false;//this指向window
// }, 3000)
// })
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
this.disabled = true;
setTimeout(function () {
this.disabled = false
}.bind(this), 3000)
}
}
call apply bind 三种方法总结
相同点
可以改变函数内部的this指向
不同点
1、call和apply 会调用这个函数 并且改变this指向
2、call和apply的传递参数不一样 call传递参数 aru1,aru2, apply必须以 数组形式[aru1,aru2]
3、bind不会调用函数 可以改变this指向
主要应用
1、call经常做继承
2、apply 经常跟数组相关,借助一些Math对象的求最大值最小值方法等的
3、bind 需要变this 但是有不直接执行函数的 比如定时器内部的this指向
严格模式
JavaScript 除了提供正常模式外,还提供了严格模式(strict mode)。ES5 的严格模式是采用具有限制性 JavaScript 变体的一种方式,即在严格的条件下运行 JS 代码。
严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的 JavaScript 语义做了一些更改:
1 消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
2 消除代码运行的一些不安全之处,保证代码运行的安全。
3 提高编译器效率,增加运行速度。
4 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class, enum, export, extends, import, super 不能做变量名
高阶函数
// 高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
//回调函数 就是高阶函数的一种
function fn(a, b, callback) {
console.log(a + b);
callback && callback();
}
fn(1, 2, function () {
alert("计算完毕")
})
// 函数作为返回值
function fun() {
return function () {
console.log(111);
}
}
var res = fun();
console.log(res);
闭包
// 变量根据作用域的不同分为两种:全局变量和局部变量。
// 1. 函数内部可以使用全局变量。
// 2. 函数外部不可以使用局部变量。
// 3. 当函数执行完毕,本作用域内的局部变量会销毁。
// 闭包 指一个函数 可以 访问另一个函数的局部变量 的 函数
function fn() {
var num = 10;
function fun() {
console.log(num);
}
fun();
}
fn();
闭包作用
// 我们fn 外面的作用域可以访问fn 内部的局部变量
// 闭包的主要作用: 延伸了变量的作用范围
function fn() {
var num = 10;
// function fun() {
// console.log(num);
// }
// fun();
return function () {
console.log(num);
}
}
var f = fn();
// f = function(){
// console.log(num);
// }
f();
闭包的应用
点击li获取索引号
// 传统方式获取索引号
var lis = document.querySelectorAll("li");
// for (var i = 0; i < lis.length; i++) {
// lis[i].setAttribute("index", i);
// lis[i].addEventListener("click", function () {
// console.log(this.getAttribute("index"));
// })
// }
//利用闭包获取索引号
for (var i = 0; i < lis.length; i++) {
// for 循环会产生4个立即执行函数
(function (i) {
lis[i].addEventListener("click", function () {
console.log(i);
})
})(i)
}
定时器里的闭包
<ul>
<li>星期一</li>
<li>星期2</li>
<li>星期3</li>
<li>星期4</li>
</ul>
<script>
var lis = document.querySelectorAll("li");
for (var i = 0; i < lis.length; i++) {
(function (i) {
setTimeout(function () {
console.log(lis[i].innerHTML);
}, 3000)
})(i)
}
</script>