function
所有函数都是构造函数 new Function() 的实例对象;
- 函数定义方式
var f = new Function("a", "b", "console.log(a+b)"); // (罕见) 生成函数f
// 函数声明 (命名函数)
function fn() {}
// 函数表达式 (匿名函数)
var fun = function () {};
console.log(f instanceof Object); // true
console.log(f.__proto__ === Function.prototype); // true
- 函数调用方式 & 内部的this指向
// 普通函数
function fn2() {
console.log(this) // window
}
// 对象方法
var obj = {
say:function(){
console.log(this); // obj
}
}
obj.say();
// 构造函数
function Start() {
console.log(this); // 实例对象
}
new Start();
// 事件函数(是异步函数哦)
btn.onclick = function(){
console.log(this); // btn元素
}
// 定时器函数
setInterval(()=>{
console.log(this); // window
});
// 立即执行函数
(function(){
console.log(this); // window
})()
函数方法call, apply, bind 改变this指向
- call调用函数并改变this,主要作用于实现继承
function fn(a, b) {
console.log(this); // obj
console.log(a+b); // 3
}
let obj = { name: "c" };
fn.call(obj, 1, 2);
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price, category) {
Product.call(this, name, price);
this.category = category;
}
let food = new Food("cheese", 5, "food");
- apply 调用函数并改变this,把数组解析成单数
function fn(val, val2, val3) {
console.log(this); // obj
console.log(val,val2,val3); // 1 2 3
}
let obj = { name: "c" };
fn.apply(obj, [1, 2, 3]);
// Math.max(1,2)只接受单个参数, 不接受数组; 利用apply把数组解析成单数
let max = Math.max.apply(null, [1,2,3]); // = Math.max(1,2,3);
console.log("max:", max);
- bind不调用函数,绑定并返回新的函数
function fn(a, b) {
console.log(this);
console.log(a + b || false);
}
var obj = { name: "c" };
var f = fn.bind(obj, 1, 2);
f(); // obj; 3
fn(); // window; false
var btn = document.querySelector("button");
btn.onclick = function () {
this.disabled = true;
let that = this;
setTimeout(
function () {
// that.disabled = false;
this.disabled = false;
}.bind(this),
3000
);
};
闭包
递归函数
函数内部自己调用自己;
var num = 1;
function fn(){
if(num == 6)return;
num++;
fn();
}
fn();
console.log(num)
function workout() {
let start = 20;
let current = 0;
let sum = 0;
return {
getTotal: function (group) {
if (current > group) {
return sum;
}
sum += start;
current++;
start++;
return this.getTotal(group);
},
};
}
var w = workout();
console.log(w.getTotal(4));
Object.defineProperty
let obj = {};
// 等价于 obj.number = 1;
Object.defineProperty(obj, 'number', {
value: 1
})
Object.defineProperty(obj, 'name', {
value: 'tom',
writable: false, // 是否可以被重写; 默认false;
enumerable: false, // 是否可被遍历; 默认false;
configurable: false, // 是否可被删除 & 这3个特性不能再次修改; 默认false不可删除;
})
obj.number = 2; // 无效
delete obj.number; // 无效
console.log(obj); // {number: 1, name: 'tom'}
console.log(Object.keys(obj)); // 无效 []
var person = {};
Object.defineProperties(person, {
name: {
value: 'tom',
writable: true,
enumerable: false,
configurable: false,
},
age: {
value: 17,
writable: true,
enumerable: false,
configurable: false,
},
});
console.log(person) // {name: 'tom', age: 17}
构造函数
-
一种特殊的函数, 用来初始化对象, 即为对象成员变量赋初始值, 与new一起使用, 我们可以把对象中一些公共的属性和方法抽取出来, 然后封装到这个函数里面;
-
创建对象三种方式:
1和2,每生成一个对象,都需要单独给对象添加属性;3则将公共属性和方法封装在一个构造函数中,new生成对象;- var obj = { } 对象字面量 注意:特俗键名必加引号;
- var obj= new Object();
- 构造函数;
-
new一个新的对象执行的步骤:
1.创建新对象{ };
2.赋值给this, 将this指向这个对象;
3.给这个新对象添加属性和方法;
4.return新对象;
function Animal(name) {
// 实例成员: 在构造函数内部通过this添加的属性or方法, 只能由实例化对象来访问.
this.name = name;
this.run = function (method) {
console.log(this.name, "is running", method);
};
}
// 静态成员: 在构造函数本身上添加的属性or方法, 只能由构造函数本身访问.
Animal.bark = () => {
console.log("roar loudly");
};
var dog = new Animal("dog");
var cat = new Animal('cat');
dog.name; // dog
dog.run("fast"); // dog is running fast
Animal.bark(); // roar loudly
prototype原型对象
-
构造函数的缺陷: 每new一个实例对象, 里面的方法(函数是引用类型)就会开辟一块内存(如果有一百个实例对象占据一百个空间,但却存储相同的属性和函数,浪费内存);
-
在构造函数的原型对象中(如Animal.prototype)添加公共的属性or方法;
实例对象.proto指向了原型对象,就能使用原型对象中的成员; -
原型对象中this & 构造函数中this指向实例对象;
Animal.prototype.bite = function(){
console.log('chips');
}
console.log(dog.run === cat.run) // false 构造函数里的方法
console.log(dog.bite === cat.bite) // true 原型对象上的方法
console.log(dog.__proto__ === Animal.prototype); // true
constructor
是构造函数的原型对象中的属性,称为构造函数, 此属性指向了此对象引用于哪个构造函数;
// Animal.prototype.bite = function(){
// console.log('chips');
// }
Animal.prototype = {
// 当用对象的形式赋值给原型对象时,指向的对象没有constructor指向构造函数;
constructor: Animal,
bite: function(){
console.log('chips');
},
drink: function(){
console.log('beer')
}
}
构造函数和prototype和__proto__关系图
原型链
实例对象.__proto指向构造函数的原型对象,
而构造函数的原型对象.__proto指向了Object构造函数的原型对象,
Object构造函数的原型对象.__proto指向null;
当访问对象的属性和方法时(就近原则), 首先查找对象自身是否有,如没有,就会根据原型链向上查找,直到最后undefined;
继承
ES6没有extends继承,而是利用函数继承父类型的属性,利用原型对象继承父类型的方法;
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price, category) {
Product.call(this, name, price);
this.category = category;
}
let food = new Food("cheese", 5, "food");
Product.prototype.sum = function (arr) {
return arr.reduce((prevValue, currentValue) => prevValue + currentValue, 0);
};
function Product(name, price) {
this.name = name;
this.price = price;
}
Food.prototype = new Product();
Food.prototype.constructor = Food;
Food.prototype.minus = function(a, b){
return a - b;
}
function Food(name, price, category) {
Product.call(this, name, price);
this.category = category;
}
let food = new Food("cheese", 5, "food");
console.log(food.name, food.price, food.category); // 继承属性
console.log(food.sum([1, 2, 3])); // 继承方法
console.log(food.minus(5,3)); // Food构造函数独有方法
分析:
son实例对象 .__proto 查找father实例对象,
father实例对象.__proto查找到father.prototype原型原型对象上的方法,就能调用了.
类
是一个抽象性的模板,对于该类事物的综合描述,这些描述中分为两种内容,一是属性,二是方法;
实例化: 使用new根据类创造出一个实例对象;
consturctor:是类的构造函数,用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如没有定义,类内部自动创建一个constructor;
注意:
1.类没有声明提升;
2.类中的方法都默认开启了严格模式
// 创建类
class Animal {
constructor(name) {
// 实例对象共有属性
this.name = name;
this.init(); // 创建实例对象就立即调用;
}
init(){
console.log('initial finished');
}
// 实例对象共有方法
run(method) {
console.log(this.name, `is runing ${method}`);
},
// 静态方法
static eat(){
console.log('chips');
}
}
// 生成实例对象
var dog = new Animal("dog");
var cat = new Animal("cat");
console.log(dog.name); // dog
console.log(cat.name); // cat
dog.run("fast");
cat.run("slow");
等价于ES5构造函数
function Animal(name) {
this.name = name;
}
// 静态方法
Animal.eat = function () {
console.log("chips");
};
// 原型方法
Animal.prototype = {
constructor: Animal,
run(method) {
console.log(this.name, " is running", method);
},
};
var dog = new Animal("dog");
Animal.eat();
dog.run("fast");
extends & super
- 子类可以继承父类的方法和属性;
- 继承后遵循就近原则,最后调用父类普通函数;
- super关键字用于访问和调用父类上的函数。可以调用父类的构造函数和普通函数;
class Father {
run(){
console.log('run')
}
say() {
console.log('f')
}
}
class Son extends Father{
say(){
console.log('s')
}
}
var s = new Son(1, 2);
s.run(); // run
s.say(); // s
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
say() {
console.log("fuck");
}
}
class Son extends Father{
constructor(x, y){
// 调用父类constructor构造函数; 等价于 Father.constructor(x,y);
super(x, y);
}
say() {
super.say(); // 调用父类普通函数;
}
}
var s = new Son(1, 2);
s.sum(); // 3
s.say(); // fuck
分析:
- new Son(1,2)传给的是son.contrutor(x,y);
- s.sum()调用,是father.sum函数, 而sum函数里的this.x指是father.constructor.x; 所以没访问到就报错;
- 利用super(x,y)调用父类构造函数 等价于调用 father.constructor(x,y), 并传递形参;
- 最后s.sum()调用,sum(){}函数中this.x就能访问到father自身上的x属性了;
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
super(x, y); // 必须在this前调用
this.a = x;
this.b = y;
}
minus() {
console.log(this.a - this.b);
}
}
var s = new Son(3, 2);
s.sum(); // 5
s.minus(); // 1
注意:
let that;
class Animal {
constructor(name) {
this.name = name;
that = this;
this.btn = document.querySelector("button");
// btn元素点击调用执行,run函数this指向的是btn元素本身;
// 利用一个全局变量,将实例对象赋值其中,这样不管谁调用run函数,that都指向的是实例对象;
this.btn.onclick = this.run;
}
run() {
console.log(this);
console.log(that.name);
}
}
var dog = new Animal("dog");
dog.run(); // 这里dog.run调用,run函数里的this, 指向dog实例对象;
example:
// let that;
class Tab {
constructor(id) {
that = this;
this.el = document.querySelector(id);
this.addBtn = document.querySelector("button");
this.ul = this.el.querySelector("ul");
this.tabContent = this.el.querySelector(".tab-content");
this.init();
}
init() {
this.getNode();
this.addBtn.onclick = this.addTab.bind(this.addBtn, this);
for (var i = 0; i < this.lis.length; i++) {
// 给每个li标签添加属性index;
this.lis[i].index = i;
// bind(null, this)在严格模式中不可以,不改变switchTab函数的this指向;
this.lis[i].onclick = this.switchTab.bind(this.lis[i], this);
this.labels[i].ondblclick = this.editTab;
this.deleteIcon[i].onclick = this.removeTab.bind(this.deleteIcon[i], this);
}
}
getNode() {
this.lis = this.el.querySelectorAll("li");
this.labels = this.el.querySelectorAll(".label");
this.deleteIcon = this.el.querySelectorAll(".close");
this.sections = this.el.querySelectorAll("section");
}
switchTab(that) {
that.clearClass();
this.className = "active";
that.sections[this.index].className = "active";
}
clearClass() {
// 这里的this是that.clearClass调用,也就是实例对象下的this;
for (var i = 0; i < this.lis.length; i++) {
this.lis[i].className = "";
this.sections[i].className = "";
}
}
addTab(that) {
that.clearClass();
// 另外一种方式: createElement, and then innerHTML赋值, appendChild;
// 此方法可以直接把 字符串格式元素 添加到父元素中;
var li = `<li class='active'>new tab<span>x</span></li>`;
that.ul.insertAdjacentHTML("beforeend", li);
var section = `<section class='active'>new tab的内容</section>`;
that.tabContent.insertAdjacentHTML("beforeend", section);
that.init();
}
removeTab(that, e) {
// 阻止冒泡到li
e.stopPropagation();
var index = this.parentNode.index;
console.log("index:", index);
// ele.remove,把对象从所属的DOM树中删除;
that.lis[index].remove();
that.sections[index].remove();
that.init();
// 移除元素后,如li中仍然有选中状态的li,就不执行移动到前一个;
if (document.querySelector("li.active")) return;
// 移除元素后,移除的元素的是选中状态li,就将选中状态移到前一个;
index--;
that.lis[index] && that.lis[index].click();
}
editTab() {
var str = this.innerHTML;
this.innerHTML = `<input type="text">`;
// 获取input元素;
var input = this.children[0];
input.value = str;
// 选定状态;
input.select();
input.onblur = function () {
// this是input触发调用;
this.parentNode.innerHTML = this.value;
};
input.onkeyup = function (e) {
e.keyCode === 13 && this.blur();
};
}
}
new Tab("#tab");
网友评论