JavaScript note

1.浏览器

IE——trident
Chrome——webkit / blink
Firefox——gecko
Opera——presto
Safari——webkit

2.数据类型

a. 原始值:
存放在栈中(stack: first in last out)(不可改变的原始值)
Number Boolean String undefined null
栈相当于内存,内存中的数据不会更改,只会依次往下一个内存单元格里储存,直到内存用 完,才开始进行新一轮覆盖。
原始值存放时把值放在栈中,然后将栈的单元格编号改为变量名。
b. 引用值:
大致存放在堆中(heap)
array Object function date RegExp…
引用值存放时把值放在堆中,把堆的单元格编号放在栈中,形成指向性的指针,然后将栈的单元格编号改为变量名。

3.下列方法后面不用加分号(;)

function test () {}
for () {}
if () {}

4.逻辑运算符(&& || !)

a. &&:先看第一个表达式转换为布尔值的结果,如果为false,则返回第一个表达式的值,如果结果为真,那么它会看第二个表达式转换为布尔值的结果;如果只有两个表达式的话,只要看到第二个表达式,就可以返回该表达式的值了。如果有多个表达式以此类推。
undefined、null、NaN、""、0、false转换为布尔值的结果都为false

var a = 1 && 2;
console.log(a); // 2

1 && document.write(‘Hello World’); //短路语句

b. || :依次看表达式转换为布尔值的结果,遇真则返回该表达式的值。
c. !

typeof()

typeof()返回六种数据类型:
number 、 string 、boolean 、undefined 、 object 、 function

console.log(typeof(null))		//object,  null为原始值,但是历史遗留问题,当初浏览器用它为对象占位;

var num = 123;
console.log(typeof num)		//number,  typeof num 这种格式不常用;
console.log(typeof(typeof(num)))		//string,  typeof()返回值的类型为string

JS运行三部曲

语法分析 ——> 预编译 ——>解释执行

预编译 四部曲

函数体系内的预编译:
1.创建AO对象(Activation Object, 作用域,执行期上下文);
2.找形参和变量声明,将变量和形参作为AO属性名,值为undefined;
3.将实参值和形参统一;
4.在函数体里找函数声明,值赋予函数体。

全局预编译
1.创建GO对象(Global Object, GO === window);
2.找形参和变量声明,将变量和形参作为GO属性名,值为undefined;
3.将实参值和形参统一;
4.找函数声明,值赋予函数体。

未经声明就赋值的变量归全局所有(归GO所有)

只有表达式才能被执行符号()执行,及立即执行函数

正确:

    function abc (){
    	var num = 123;
    	console.log(num);
    }
    abc();   //正确

错误:

    function abc (){
    	var num = 123;
    	console.log(num);
    }()	//错误, 此为函数声明

表达式定义广泛,除了非表达式,都是表达式!

abc		//表达式
123		//表达式
abc = funciton () {}		//表达式
i++		//表达式
function test () {}		//非表达式,函数声明;

计算符号可以使函数声明变为表达式,如下:

+function test () {}		//表达式
-function test  () {}		//表达式
!function test () {}		//表达式

由此可得到立即执行函数(立即执行函数的不规范格式):

// ()让函数声明变为表达式
(function test () {})		//表达式
//即为表达式,便可执行
(function test () {
	console.log('a')
})()		//正确,可执行

立即执行函数的标准格式和不标准格式:

(function () {} ())		//标准
(function () {})()		//不标准

标准格式中的最外层()为数学运算符号,优先级高;{}后面的()为执行符号,优先级低,故和非标准格式一样;

如下表达式被执行后,就会失去对原来函数的索引:

var demo = function () {
	console.log('a');
}()
console.log(demo);			//undefined

if(function foo (){}){
	console.log(typeof foo);		//报错
	console.log(typeof foo);		//undefined
}

但凡可以把function变成表示式的,都会让function失去对自身的索引。

闭包

当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。
闭包:

function test(){
	var arr = [];
	for(var i = 0; i < 10; i++){
		arr[i] = function(){
			console.log(i);
		}
	}
	return arr;
}

var myArr = test();
for(var j = 0; j < 10; j++){
	myArr[j]();
}
//执行结果为console了10个10。因为console.log(i)这个函数并没有被执行,而是保存到了arr中,形成闭包,这个函数的作用域链(AO)没有释放,保存出来的i已经被i++为10

利用立即执行函数和闭包原理解决闭包问题

function test(){
	var arr = [];
	for(var i = 0; i < 10; i++){
		(function (j) {
			arr[j] = function(){
				console.log(j);
			}
		}(i))
	}
	return arr;
}

var myArr = test();
for(var j = 0; j < 10; j++){
	myArr[j]()
}		//执行结果为console了0,1,2,3,4,5,6,7,8,9

闭包案例:

打印被点击的li的索引
<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
</ul>
错误:
function demo () {
	var Lis = document.getElementsByTagName('li');
	for(var i = 0; i < Lis.length; i++){
			Lis[i].onclick = function(){
				console.log(i);
			}
	}
}
demo();

正确(利用闭包):
function demo () {
	var Lis = document.getElementsByTagName('li');
	for(var i = 0; i < Lis.length; i++){
		(function(j){
			Lis[j].onclick = function(){
				console.log(j);
			}
		}(i));
	}
}
demo();

正确(利用ES6的let):
function demo (){
	var Lis = document.getElementsByTagName("li");
	for(let i = 0; i < Lis.length; i++){
		Lis[i].onclick = function(){
			console.log(i);
		}
	}
}
demo();

对象

对象有属性,有方法

var andy = {
	name: '属性';
	health: 100;
	run: function (){		//方法
		console.log('I like running !');
		this.health++;
	};
	drink: function(){		//方法
		console.log('I like drinking !');
		this.health--;
	}
}
andy.run		//表示函数的引用;
andy.run()		//表示函数的执行;

1. 对象属性的增、删、改、查

	增加属性: andy.wife = 'Nancy';
	删除属性: delete andy.drink;
	修改属性: andy.wife = 'Kelly';
	查询属性: andy.name;
	
	当一个变量没有经过定义使用的话会报错;
	当一个对象的属性没有定义便访问的话会打印undefined

2. 对象的创建方法

  1. var obj = {} plainObject 对象字面量/对象直接量
  2. 构造函数
    1. 系统自带的构造函数 new Object()
      使用方法如:var obj = new Object();

    2. 自定义

      function Student ( name, age, sex){
      	this.name = name;
      	this.age = age;
      	this.sex = sex;
      	this.grade = 2018;
      }
      var student = new Student( 'Andy', 18, 'male');
      
    构造函数必须遵循大驼峰式命名方法

构造函数内部原理

构造函数三段式,前提是前面必须加上new

  1. 在函数体最前面隐式的加上var this = {};

  2. 执行this.xxx = xxx;

  3. 隐式的返回this

    function Student ( name, age, sex){
    	this.name = name;
    	this.age = age;
    	this.sex = sex;
    	this.grade = 2018;
    }
    var student = new Student( 'Andy', 18, 'male');
    
    一旦执行了new,就有如下步骤:
    function Student ( name, age, sex){
    	//1. 在函数体最前面隐式的加上this = {};
    	// var this = {};
    	this.name = name;
    	this.age = age;
    	this.sex = sex;
    	this.grade = 2018;
    	//2. 执行this.xxx = xxx;
    	//var this = {
    	//	this.name = name;
    	//	this.age = age;
    	//	this.sex = sex;
    	//	this.grade = 2018;
    	//}
    	//3. 隐式的返回this
    	//return this;
    }
    

包装类

属性和方法只有对象能有,对象包括对象自己、数组、函数;
原始值是没有属性和方法的。

数字有原始值型数字和对象型数字,字符串、布尔值也是;
对象型的可以有属性和方法。
undefined 、null 不可以有属性和方法。

当一个变量没有经过定义使用的话会报错;
当一个对象的属性没有定义便访问的话会打印undefined

var num = 123;  	//原始值型
console.log(num);
var num = new Number(123);		//对象型数字,可以有属性、对象,也可以参与运算,但是运算结束后又变成原始值型数字
console.log(num);
num = num * 2;
console.log(num)

字符串有个系统自带的属性length。(其实也是系统隐式的进行了包装类)

var str = 'abc;
console.log(str.length); 	//3

原始值没有属性和方法,但是执行如下代码并不会报错:

var str = 'abc';
str.abc = 'a';			    //此时刷新页面,执行不会报错
console.log(str.abc);		//undefined(不报错)

var  num = 3;
num.len = 5;				//此时刷新页面,执行不会报错
console.log(num.len);		//undefined(不报错)

以上代码执行时有一过程叫——包装类,如下:

//包装类
var  num = 3;
num.len = 5;
//原始值不能有属性,当调用属性时会发生一个隐式的过程
//系统会新建一个数字对象,然后执行上边的属性操作,执行完后会自行消除掉
//new Number(3).len = 5;  delete
console.log(num.len);
//当你再次调用这个属性时,系统会再次新建一个数字对象,然后执行属性的访问操作,执行完后再消除掉
//new Number(3).len
//由于构造函数创建的对象是各自独立的,所以这个对象并没有len属性,所以打印结果为undefined

这个隐式的过程叫做包装类。
常见骗术

var arr = [1, 2, 3, 4, 5];
arr.length = 2;
console.log(arr);		//[1, 2]

//基于以上结果,如下类似的是否成立

var str = 'abcd';
str.length = 2;
console.log(str);
//结果为'abcd',因为字符串为原始值,执行过程经历了包装类。

案例:

var str = 'abc';
str += 1;
var test = typeof(str);
if(test.length = 6){
	test.sign = 'typeof的返回值可能为String';
}
console.log(test.sign);

考题:

var x = 1, y = z = 0;
function add(n){
	return n = n + 1;
}
y = add(x);
function add(n){
	return n = n + 3;
}
z = add(x);
console.log(x, y, z)		//1, 4, 4   执行预编译,函数提升,不要看表面书写顺序
//下面代码中console.log结果是[1, 2, 3, 4, 5]的选项是()
//A
function foo(x){
	console.log(arguments);
	return x
}
foo(1, 2, 3, 4, 5);

//B
function foo(x){
	console.log(arguments);
	return x;
}(1, 2, 3, 4, 5)

//C
(function foo(x){
	console.log(arguments);
	return x;
})(1, 2, 3, 4, 5)

//D
function foo(){bar.apply(null, arguments)}
function bar(x){console.log(arguments)}
foo(1, 2, 3, 4, 5)

//A C D

//以下表达式的执行结果是()
parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)
//A
3 3 3
//B
3 3 NaN
//C
3 NaN Nan
//D
other

// C和D都可以, parseInt(3, 0)在不同浏览器中执行结果不一样,0为基底的不同执行结果
//封装一个方法,求字符串的字节长度,提示:unicode > 225时字节长度为2,unicode  <= 225时字节长度为1

//case 1
function getCodeLen (str) {
	var len = 0;
	for(var i = 0; i < str.lenth; i++){
		if(str.charCodeAt(i) > 225){
			len += 2;
		}else{
			len++;
		}
	}
	return len;
}

//case 2
function getCodeLen2 (str) {
	var len = str.length;
	for(var i = 0; i < str.length; i++){
		if(str.charCodeAt(i) > 225){
			len++;
		}
	}
	return len;
}

undefined、null没有包装类。

原型

原型:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。

利用原型特点和概念,可以提取共有属性。

对象如何查看原型——>隐式属性__proto__

对象如何查看对象的构造函数——>constructor

原型自己可以增删改,后代不可以对原型进行增删改。后代利用xxx.xxx直接修改算特例,其实相当于原型本身自己改,因为这样直接修改了引用值的堆单元格里的值。(后面有例)

原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
例子:

function Student () {
}
//Student.prototype —— 原型
//Student.prototype = {} ——是祖先
Student.prototype.school = '北京大学';
var zhangSan = new Student();
var liSi = new Student();
console.log(zhangSan.school ,  liSi.school);

原型里有而构造函数本身没有的属性,访问时取原型上的;原型里有而构造函数本身也有的属性,访问时取函数本身的。

Student.prototype.grade = 1;
function Student (){
	this.grade = 2;
}
var wangWu = new Student();
console.log(wangWu.grade);

利用原型特点和概念,可以提取共有属性。

Car.prototype.brand = 'BMW';
Car.prototype.lenght = 5200;
Car.prototype.height = 1500;
Car.prototype.width = 1850;
function Car (color, owner){
	this.color = color;
	this.owner = owner;
}
var car1 = new Car('white', 'Andy');
var car2 = new Car('blue', 'Bale');
console.log(car1.brand, car2.brand);

通过赋值可以修改当前构造函数本身的属性,但是无法修改原型的属性。如修改上例中的属性须使用:Car.prototype.brand = ‘Benz’
在构造函数中对原型的增删改查,只有查可以实现。
原型的简化写法:

Car.prototype = {
	brand: 'BMW';
	length: 5200;
	height: 1500;
	width: 1850;
}
function Car (color, owner){
	this.color = color;
	this.owner = owner;
}
var car1 = new Car();

对象如何查看对象的构造函数——>constructor

function Car (color, owner){
	this.color = color;
	this. owner = owenr;
}
var car1 = new Car();
//控制台输入
//car1.constructor
//得到Car构造函数

对象的构造函数可以修改:

function Car(color, owner){
	this.color = color;
	this.owner = owner;
}
function Student(){
	this.name = Jack;
}
var car1 = new Car();
//修改方法1
Car.prototype = {
	constructor: Student;
}
//修改方法2
car1.constructor = Student;

//控制台输入car1.constructor等到Student函数

对象如何查看原型——>隐式属性_proto_
对于前面的构造函数的三段式:
构造函数三段式,前提是前面必须加上new,1 在函数体最前面隐式的加上var this = {};2. 执行this.xxx = xxx;3. 隐式的返回this
其中第一步中加的var this = {}, 并不是空对象,而是含有__proto__的对象,其中__proto__相当于桥梁、连接指向

Car.prototype.brand = 'BMW';
function Car () {
	//var this = {
	//	__proto__: Car.prototype;
	//}
}
var car1 = new Car();
//所以car1.brand可以访问

这个桥梁/连接指向是可以修改的:

var obj = {
	brand: 'Benz'
};
Car.prototype.brand = 'BMW';
function Car () {};
var car1 = new Car();
console.log(car1.brand);		//结果为BMW

car1.__proto__ = obj;
console.log(car1.brand);		//结果为Benz

//但是并没有改变构造函数的原型
var car2 = new Car();
console.log(car2.brand);		//结果为BMW

手动修改原型时的问题:
直接用xxx.prototype.xxx = xxx修改

Car.prototype.brand = 'BMW';
function Car () {}
var car1 = new Car();
Car.protoype.brand = 'Benz';
console.log(car1.brand);		//结果为Benz

用xxx.prototype = { xxx: xxx}修改

Car.prototype.brand = 'BMW';
function Car () {}
var car2 = new Car();
Car.prototype = {
	brand: 'Benz'
}
console.log(car2.brand);		//结果为BMW

//修改失败原因如下:
var obj = {name: 'a'};		//引用值
var obj1 = obj;
obj = {name: 'b'};
console.log(obj1);		//obj1的值并不会改变
//xxx.prototype = {xxx: xxx}方法亦是如此
var obj = {name: 'a'};		//引用值
var __proto__ = obj;
obj = {name: 'b'};
console.log(__proto__);		//__proto__的值并不会改变
//执行步骤:
//Car.prototype.brand = 'BMW';
//function Car () {
//	var this = {__proto__: Car.prototype}
//此时__proto__指向的和Car.prototype是一个空间		
//}
//var car2 = new Car();
//Car.prototype = {
//	brand: 'Benz'
//此时Car.prototype更换了空间,而__proto__指向的空间并没有更换,还是原来的空间,所以值仍为原来的BMW
//}

xxx.prototype.xxx = xxx与xxx.prototype = { xxx: xxx}修改的方法和过程是不一样的,前者直接修改本对象指向的堆中的单元格里的值,后者是在堆中新的单元格中存放一个新的值,然后改变本对象的指针(在栈中存放的堆的单元格编号),使其指向新的单元格。详见原始值与引用值。

函数执行顺序问题,更换xxx.prototype = { xxx: xxx}位置,即可修改成功:

Car.prototype.brand = 'BMW';
function Car () {}
Car.prototype = {
	brand: 'Benz'
}
var car2 = new Car();
console.log(car2.brand);		//Benz
//函数Car()在new的时候才执行,而此时Benz已经修改了prototype的指针,所以__proto__指向了新的Benz

原型自己可以增删改,后代不可以对原型进行增删改。后代利用xxx.xxx直接修改算特例,其实相当于原型本身自己改,因为这样直接修改了引用值的堆单元格里的值。

原型链

原型还可以有原型,原型连成串,就是原型链。
原型链的最顶端是Object.prototype。最终原型
原型链上属性的增删改查
绝大多数对象的最终都会继承自Object.prototype
Object.create(原型)

Person.prototype = {
	name: 'a',
	sayName: function () {
		console.log(this.name);
	}
}
function P
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值