参考链接:学习JavaScript这一篇就够了_轻松的小希的博客-CSDN博客
1. 标识符
起名规则:
第一个字符必须是字母,下划线或者$
其它字符可以是字母,下划线,$,数字
驼峰命名法,第一个单词小写,之后的单词首字母大写
不能是关键字和保留字符
2. 变量
变量声明, 与C语言类似,在C中,需要声明使用数据类型,但是变量类型为var,可以保存任何类型的数据,若没有初始化,则会保存undefined
var a;
3. 数据类型
JS中5种基本数据类型:
string number boolean undefined null,这5种之外都为Object
可以用typeof获取数据类型。
typeof xxx
特殊的数字,属于number类型,Infinity, -Infinity, NaN (Not A Number) 分别是正无穷,负无穷,还有非法数字。
undifined类型只有一个值,即为undifined,也就是说这个类型是undefined,值也是undifned。
Null类型也只有一个值,即为null,undifined由null衍生出来,比较undefined和null是否相等,会返回true。用typeof检查null会返回Object,表示一个空的对象。
3.1 数据类型强制转换
当给null使用toString时报的错,不支持该方法,使用String() or +""
// cast to string, notice the capital "S" in String()
var a = 123;
a = a.toString();
b = String(a);
c = a + "";
console.log(a, b, c);
// for null and undefined type, toString() is not available
// there will be an error
var a1 = null;
var a2 = undefined;
a1 = String(a1);
a2 = String(a2);
// cast to number
var b1 = "1234";
// Number(0 can be used to cast any type of data to number type
b1 = Number(b1);
// only be used when operation object type is String and the result will be //integer.
b1_1 = parseInt(b1);
//transfer a string to float
b1_2 = parseFloat(b1);
// cast to boolean
Number c1 = 1;
c1 = Boolean(c1);
3. 运算符
没有接触过的,其他的运算符类似:
全等 ===,和相等==的区别:
== 会自动做类型转换,如果式子两端的类型不同,会自动转换为同一类型进行比较
=== 则不会自动做类型转换,如果两个值的类型不同 直接返回false
不全等 !== 与!= 的关系和全等与相等类似
4. 对象基础
对象,类似C语言种的结构体,会包含对象的一些共有属性。
Object类型是JS中的引用数据类型,可以将很多值聚合到一起,通过名字进行访问,每个属性都是一个名字+值的数据对。对象可以创建自有属性,也可以从名为原型的对象那里继承属性。
两种创建对象的方式
// the first method
var student = new Object();
student.name = "Jack";
student.sex = "male";
student.age = 13;
// the second method
var student_1 = {
name: "Jack",
sex: "male",
age: 18
};
两种访问属性的方式:
student.name
// or
student['name']
删除属性:
delete student.name
遍历对象
// loop all keys in student object
for (var studentKey in student)
{
var studentValue = student[studentKey];
console.log(studentKey + ":" + studentValue);
}
5. 引用数据类型
引用数据类型是保存在内存中的对象。 当一个变量是一个对象Object时,实际上变量中保存的并不是对象本身,而是对象的引用。
当从一个变量向另一个变量复制引用类型的值时,会将对象的引用复制到变量中,并不是创建一个新的对象。 这时,两个变量指向的是同一个对象。因此,改变其中一个变量会影响另一个。
6. 栈 堆
栈内存 - 保存变量和基本类型(Number boolean 等), 先进后出,后进先出
堆内存 - 保存对象
6. 函数
JS中的函数时一个对象, 用typeof检查函数时,会返回function
6.1 函数创建的几种方式
使用函数对象创建一个函数
var testF = new Function("statement");
使用函数声明创建一个函数
function testF ([param1, param2, param3,...,paramN])
{
statement;
}
使用函数表达式来创建一个函数2, 即匿名函数
var testF = function([param1, param2,..., paramN])
{
statement;
}
使用函数名进行调用
一个函数可以作为一个对象的属性进行保存,这个函数则是这个对象的方法
this对象,解析器在调用函数时,每次都会向函数内部传递一个隐含的参数,即this,this指向一个对象,根据函数的调用方式不同,this 会指向不同的对象,
以函数的形式调用时,this 为 window
以方法的形式调用时,this 为调用方法的那个属性所属于的那个对象
7. 对象进阶
7.1 创建多个具有相同属性的对象时的方法
创建多个对象,使用工厂模式。
function createStudent(name, age){
// create new object
var obj = new Object();
// set attributes
obj.name = name;
obj.age = age;
// set method
obj.printName = function() {
console.log(this.name);
};
return obj;
}
// loop
for (var i = 1; i <= 1000; i++)
{
// It's difficult to set " unique string" name, like Jack, Rose
var student = createStudent("student" + i, 18);
console.log(person);
}
用构造函数来创建对象,每个构造函数类似一个Class,每一个student都属于Student这个class, 用这种方法创建的对象被称为类的实例。
构造函数和普通函数的区别:调用方式不同,普通函数直接调用,但构造函数需要用new关键字来调用
1. 调用构造函数,会立刻创建一个新的对象
2. 将新建的对象设置为函数中的this, 在构造函数中可以使用this来引用新建的对象
3. 逐行执行函数中的代码
4. 将新建的对象作为返回值返回
与工厂创建方法相比:隐藏了创建对象(var objt = new Object() ) 和返回对象 (return Obj).
使用同一个构造函数创建的对象,称为一类对象,所以也将一个构造函数称为一个类。
通过一个构造函数创建的对象,称为该类的实例(instance)
以构造函数的形式调用时,this是新创建的那个对象Student
function Student(name, age)
{
// attribute
this.name = name;
this.age = age;
// method
this.printName = function (){
console.log(this.name)
};
}
var student1 = new Student("Jack", 18);
var student2 = new Student("Rose", 18);
7.2 原型
使用构造函数的方式创建多个对象,会保存多个相同的方法--> this.printName, 当创建的对象数量非常多时,会占用极大的内存。所以,将该方法的函数抽取出来,作为全局函数,直接在构造函数中引用。
// extract method as a global function
function printName()
{
console.log(this.name);
}
function Student(name, age)
{
this.name = name;
this.age = age;
this.printName = printName;
}
如果出现同名方法,会导致conflict。只在Student这个类的全局度喜庆中添加该函数,然后在类中引用-->在原型对象中添加, 之后直接在实例中使用,student1.printname();
Student.prototype.printName = function()
{
console.log(this.name);
}
原型 prototype
每创建一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,即原型对象(显式原型),相当于一个公共的区域(一块公共的内存,地址是同一个),所有的同一个类的实例都可以共享的区域,都能够访问到这个原型对象,所以可以将一类对象中共有的内容,统一设置到原型对象中。
当函数作为普通函数被调用时,prototype没有作用
当函数作为构造函数被调用时,该函数所创建的对象中都会含有一个隐含的属性,指向该构造函数的原型对象,可以通过__proto__(隐式原型)来访问该属性。当访问对象的一个属性或方法时,规则是现在对象自身中寻找,如果有则直接使用,没有则到原型对象中寻找,如果找到则直接使用。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
7.4 原型链
上面👆所说的,访问一个对象的属性时,先在对象自身属性找,找不到的话通过__proto__来访问公共区域内的属性,如果最终还没有,就返回undefined。这一搜索or访问顺序称为隐式原型链,作用就是查找对象的属性偶or方法。原型对象也有原型,顺着这样一直往下找,直到找到Object对象的原型,Object的原型为空,返回值为undefined。用构造函数创建的最原始的对象就是Object对象。
7.5 toString方法
该方法属于Object对象(最原始的那个对象,而不是对象实例),功能是将当前对象以字符串的形式返回。该方法属于内置的方法,不需要自己定义。对于类型为Object的变量,返回[object, ObjectName], ObjectName是对象类型的名称,类型可以是Number, Boolean等
ObjectType JS对象分类_js 对象类型_跨越海沟的博客-CSDN博客
JavaScript中的toString方法详解_js tostring()方法_周晓风的博客-CSDN博客
7.6 hasOwnProperty()
功能: 检查自身对象是否含有某个方法或属性,属于Object原型中(最初创建的对象的原型)的属性
hasOwnProperty("属性名"); 返回值为布尔
7.7 对象继承
JS通过prototype来实现面向对象编程,而不是类
继承,可以使子对象使用父对象的属性和方法,从而简化一部分代码
6种继承方式,
- 原型链继承
- 使用构造函数继承
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
1.原型链继承
子类型的原型prototype为父类型的一个实例
通用方法,
- 用构造函数创建一个父类型的原始对象
- 给父类型对象的原型添加方法 (定义另一个函数)
- 定义子类型的构造函数
- 创建父类型的一个对象实例,将其赋值给子类型的原型,用于通过原型链搜索到添加在父类型中的方法
- 将子类型的原型的构造属性constructor设置为子类型
- 给子类型的原型添加方法
- 创建子类型的对象实例,调用父类型的方法和子类型的方法,发现可以实现继承。
局限性
// 定义父类型构造函数
function SupperType() {
this.supProp = 'Supper property';
}
// 给父类型的原型添加方法
SupperType.prototype.showSupperProp = function () {
console.log(this.supProp);
};
// 定义子类型的构造函数
function SubType() {
this.subProp = 'Sub property';
}
// 创建父类型的对象赋值给子类型的原型
SubType.prototype = new SupperType();
// 将子类型原型的构造属性设置为子类型
SubType.prototype.constructor = SubType;
// 给子类型原型添加方法
SubType.prototype.showSubProp = function () {
console.log(this.subProp)
};
// 创建子类型的对象: 可以调用父类型的方法
var subType = new SubType();
subType.showSupperProp();
subType.showSubProp();
注意出错点: 拼写错误,新声明的对象实例,在后续引用时候名字保持一致
2. 借用构造函数继承
.call() 和 .apply() 将父类构造函数引入子类函数,使用父类的构造函数来增强子类实例,等于复制父类的实例给子类
步骤:
- 构建父类函数,创建父类的方法
- 构建子类函数,在子类中应用call函数,继承父类的属性,fatherType.call(this, name);相当于将子类的名字作为一个入参传入到父类构建函数,当创建实例的时候,调用父类方法,单参数是子类的
- 把子类方法加入到子类的原型中
- 创建子类的实例,然后调用各种方法
function fatherType(name)
{
this.name = name;
this.showFatherName = function()
{
console.log(this.name);
};
}
function sonType(name, age)
{
// use call to extend the attributes from fatherType
fatherType.call(this, name); // this is father object, name is from sonType
// but when use showFatherName method, it can use this method to show son's name.
this.age = age;
}
sonType.prototype.showSonName = function()
{
console.log(this.name);
}
var sonType1 = new sonType('Jack', 12);
sonType1.showFatherName();
sonType1.showSonName();
console.log(sonType1.name);
console.log(sonType1.age);
无法继承父类型的原型内的属性和方法,只能继承父类型的实例属性和方法
3. 组合继承 (最常用的继承模式)
原型链 + 借用构造函数
步骤
- 利用原型链实现对父类型对象的方法继承
- 利用super()借用父类型构建函数初始化相同属性
这种方法导致父类中的实例属性和方法既存在于子类的实例中,也存在于子类的原型中,造成重复
function flower(name, color)
{
this.name = name;
this.color = color;
this.showName = function(){
console.log(this.name);
};
}
flower.prototype.setNewColor = function(newColor)
{
// this means flower
this.color = newColor;
}
function goods(name, color, price)
{
flower.call(this, name, color);
this.price = price;
}
// the order of adding function should be after the two
// otherwise there will be an error that setPrice is not a function
goods.prototype = new flower();
goods.prototype.constructor = goods;
goods.prototype.setPrice = function(price)
{
this.price = price;
}
var goods1 = new goods("rose", "red", 20);
console.log(goods1.name, goods1.color, goods1.price);
goods1.setNewColor("yellow");
goods1.setPrice(30);
goods1.showName();
console.log(goods1.color, goods1.price);
4. 垃圾回收
GC garbage cycle, 处理程序运行过程中产生的垃圾
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。 dead code? 声明了,但未被实际使用
在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作,我们需要做的只是要将不再使用的对象设置null即可。
var flower1 = new flower("rose","red");
// GC
flower1 = null;
8. 作用域
8.1 声明提前
变量声明提前-- 使用var,不管在哪个位置使用,会在所有代码被执行前声明,如果不用var声明,则变量不会被声明提前
函数的声明提前 -- 使用函数声明形式创建的函数 function () {}会被声明提前,提前到所有代码执行之前,所以,在声明该函数的语句之前,可以调用该函数
使用函数表达式创建的函数,不会被声明提前
// 函数声明形式
function sum(i, j)
{
return i + j;
}
// 函数表达式
var sum = function(i, j){
return i + j;
}
8.2 全局作用域
在全局作用域中有一个全局对象window,代表浏览器的窗口,由浏览器创建
在全局作用域中创建的变量都会作为window对象的属性保存,创建的函数都会作为window对象的方法保存
在打开页面时,创建全局作用域,关闭页面时销毁全局作用域。
直接编写在script变迁中的JS代码,属于全局作用域
8.3 函数作用域
调用函数时创建的作用域,函数执行完毕后销毁。
每调用一次函数就会创建一个新的作用域,它们之间是相互独立的。
在函数中想要访问全局变量可以使用window对象
在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
8.4 作用域链
多个上下级关系的作用域形成的链,方向:从下到上,从内到外
查找变量时,现在自身作用域中寻找,如果有,就直接使用,如果没有就向上一级作用域寻找,以此类推,直到找到全局作用域,如果依然没有,则报错referenceError