基础
常用浏览器及对应内核
浏览器 | 内核 |
---|---|
IE | trident |
Chrome | webkit/blink |
Firefox | Gecko |
Opera | presto |
Safari | webkit |
JS特点
- 解释性语言,单线程,脚本语言。
- 动态语言基本都是解释性语言,解释性语言基本都是脚本语言。
- 解释性语言特点:解释一行执行一行。
- 解释之前会通篇扫描一遍看有没有语法错误(低级错误),如果此时有错,则程序一行都不会执行。
- 执行到逻辑错误时,终止。注释该错误则程序继续往下走。
语法错误
- 低级错误(语法分析错误)
Uncaught SyntaxError:
- 逻辑错误(标准错误)
Uncaught ReferenceError:
数据类型
- 基本类型:
number
、boolean
、string
、undefined
、null
、Symbol(ES6引入的)
- 引用类型:
array
、object
、function
、
// 原始值不能有属性
var num = 1;
num.a = 2; // 讲道理是会报错的,但是返回值是undefined。
// 原因是包装类! 所以会做这样的隐式转换:new Number(1).a = 2;
// 引用值可自由设置属性
var obj = {};
obj.a = 3;
- 两种值唯一的不同是:赋值形式不同。
// 1. 原始值放在 栈(stack) 里面; 栈属性:先进的后出
var a = 1;
b = a;
a = 2;
console.log(b) // 1
// 2. 引用值放在 堆(heap) 里面;引用的是地址
var arr = [1];
var arr1 = arr;
arr.push(2);
// 此时 arr1 == [1,2]
var arr = [1];
var arr1 = arr;
arr = [2];
// 此时 arr1 == [1]。因为arr新开了一个房间
typeof 返回六种值
- 返回的值均为字符串类型
number
、boolean
、string
、object
、function
、undefined
六种情况下返回值为 false
false
、0
、undefined
、null
、""
、NaN
===
和 ==
==
会先试图类型转换,然后再比较,而===
不会类型转换。
1 == '1' // true
1 === '1' // false
0 == false // true
0 === false // false
null == undefined // true
null === undefined // false
- 何时使用
===
何时使用==
? 根据 jQuery 源码中的写法,只推荐在一个地方用==
,其他地方都必须用===
。
// 这里相当于 obj.a === null || obj.a === undefined ,简写形式
if (obj.a == null) {}
- 运算规则
undefined == null
,结果是true。且它俩与所有其他值比较的结果都是false。String == Boolean
,需要两个操作数同时转为Number。String/Boolean == Number
,需要String/Boolean转为Number。Object == Primitive
,需要Object转为Primitive(具体通过valueOf和toString方法)。
JS中有哪些本地、内置、宿主对象?
- 本地对象:
Object
Array
Boolean
Number
String
Function
等可以new实例化。 - 内置对象:
gload
Math
等不可用实例化的。 - 宿主对象:浏览器自带的
document
window
等。
类型转换
Number(null) == 0
Number(undefined) == NaN
Number('a') == NaN
Number(a) // 报错
+undefined == NaN
onload
window.onload
是在 DOM 树加载完和所有文件加载完之后执行的一个函数,后执行,只能执行一次。document.ready
是在 DOM 树加载完之后执行的函数(不需要全部文件加载完),先执行,可出现多次。
iframe
- 优点
- 能够原封不动的把嵌入网页展现出来。
- 遇到的加载缓慢的第三方内容如图标、广告,可以由 iframe 来解决。
- 重载页面时不需要重载整个页面,只需要重载页面中的一个框架页。
- 缺点
- 页面样式调试麻烦,出现多个滚动条。
- 浏览器后退按钮失效。
- 过多会增加服务器的HTTP请求。
- 产生多个页面,不易管理。
- 代码复杂,无法被一些搜索引擎解读。
- 父子通信
// 父页面,接收子页面传值
window.onmessage = function(e) {
console.log(e.origin, e.data);
}
// 子页面,子页面给父页面传值
parent.postMessage(theObj, '*');
事件委托
利用冒泡的原理,把事件加到父级上,触发执行效果。
// 例如有一个无限滚动列表,对每个li执行事件
<ul id="list">
<li class="li-1">1</li>
<li class="li-2">2</li>
<li class="li-3">3</li>
...
</ul>
$('#list').click((e) => {
console.log(e.target.getAttribute('class'))
})
JS运行逻辑
- JS运行三部曲:
- 语法分析
- 预编译
- 解释执行
- 语法分析 通篇执行一遍,看有没有问题,有问题直接报错。
- 预编译 预编译发生在函数执行的前一刻:
- 创建 AO 对象;(AO:Activation Object 执行期上下文)
- 找形参和变量声明,将变量和形参名作为 AO 属性名,值为 undefined。
- 将实参值和形参统一。
- 在函数体里面找函数声明,值赋予函数体。
注意:全局预编译:window === GO。
先执行 GO 再执行 AO。
案例
// 预编译 实例,执行过程
function fn(a) { // 开始从预编译中找值
console.log(a); // function a(){}
var a = 123; // var a 可忽略,因为已经在预编译中执行过, a = 123; 赋值。
console.log(a); // 123
function a() {}; // 忽略
console.log(a); // 123
var b = function() {}; // var b 可忽略,因为已经在预编译中执行过, b = funciton(){}); 赋值。
console.log(b); // function(){}
function d() {};
};
fn(1);
// 执行逻辑:
// 第一步、第二步:
AO: {
a: undefined,
b: undefined,
}
// 第三步:
AO: {
a: 1,
b: undefined
}
// 第四步:
AO: {
a: function a() {},
b: function() {},
d: function d() {}
}
递归
递归就是 return 公式;只是让代码变得简洁;
- 找规律
- 找出口
// 阶乘
// 3! = 3 * 2!
// 2! = 2 * 1!
// 规律:n! = n * (n - 1)!
function mul(n) {
if(n == 1 || n == 0) {
return 1; // 出口
}
return n * mul(n - 1)
}
// 斐波那契
function mulFei(n) {
if(n == 1 || n == 0) {
return 1;
}
return mulFei(n - 1) + mulFei(n - 2)
}
立即执行函数
针对初始化功能的函数,执行完立即销毁,不占用内存。js中唯一一个可以手动立即销毁的函数。
// 写法一
(function (){}()) // 官方推荐
// 写法二
(function (){})()
(function(a,b,c) {
console.log(a + b + c * 2);
}(1,2,3));
// 可以有返回值
var num = (function(a,b,c) {
var d = a + b + c;
return d;
}(1,2,3));
- 只有表达式才能被执行符号执行
// 错误
function test() {
var a = 1;
}()
// 下方则是正确的写法,加个 + 或者 -,变为了可执行的表达式
+function test() {
console.log('1')
}() // 1
// 表达式被执行后会忽略前面的名字
var test = function() {
console.log('a')
}() // a
注意:
// 函数声明不会执行,所以此刻是没用的,返回a是因为('a')
function test(a) {
console.log(a)
}('a') // a
如何理解JSON
JSON 是一个js对象,是一种数据格式。
// 将对象转为字符串
JSON.stringify({a:10, b:20})
// 将字符串转为对象
JOSN.parse('{"a":10,"b":20}')
数组
常用方法
let arr = [1,2,3];
// push:尾部添加;可添加一个也可添加多个
arr.push(4);
// pop:尾部删除
arr.pop();
// unshift:头部添加
arr.unshift(0);
// shift:头部删除
arr.shift();
// reverse:翻转
arr.reverse();
// splice:截取(从第几位,截取几个,添加新数据)
arr.splice(0, 1, 100);
// concat: 组合
arr.concat(arr1,arr2)
// sort:排序
arr.sort(function(a,b) {
return a - b; // 升序
return b - a; // 降序
return Math.random() - 0.5; // 乱序
})
去重
let arr = [1, 2, 2, 3, 3, 4, 5, 6, 6];
// 方法一
let arr1 = [...new Set(arr)];
// 方法二
let arr2 = [];
arr.forEach(function(item, index) {
// if (arr2.includes(item)) {
if (arr2.indexOf(item) != -1) {
arr2.splice(index, 1)
} else {
arr2.push(item)
}
})
最大值
let zuidArr = [1, 2, 3, 44, 5, 6];
console.log(Math.max(...zuidArr))
console.log(Math.max.apply(null, zuidArr))
字符串转数组
let str = 'goodmorening';
let strArr1 = Array.from(str);
let strArr2 = str.split('');
let strArr3 = [...str];
let strArr4 = Array.prototype.slice.call(str);
let strArr5 = [].slice.call(str);
对象
- 特点:有属性,有方法
var mrFu = {
name: 'fj', // 属性名属性值,或者叫key值value值
age: 27,
health: 100,
smoke() {
this.health--;
},
drink() {
this.health++
}
}
// 1、 增:
mrFu.wife = 'xiaoyang';
// 2、 查:
mrFu.name;
// 3、 改:
mrFu.age = 28;
// 4、 删:
delete mrFu.age;
- 对象的枚举
var obj = {
name: 'fj',
age: 27,
__proto__: {
lastName: 'xx'
}
}
Object.prototype.protoName = 'yjx'
for (let key in obj) {
console.log(key) // name age lastName protoName
// 不从原型链上寻找key值
if (obj.hasOwnProperty(key)) {
console.log(key) // name age
}
}
console.log('name' in obj) // true
闭包
- 闭包是指有权访问另一个函数作用域中的变量的函数。
- 但凡是内部的函数,被保存到了外部,一定产生闭包。
- 自由变量将从作用域链中去寻找,但是依据的是函数定义时的作用域链,而不是函数执行时。
- 函数作为返回值。
function F1() {
var a = 100
return function () {
console.log(a)
}
}
var f1 = F1()
var a = 200
f1()
- 函数作为参数传递。
function F1() {
var a = 100
return function () {
console.log(a)
}
}
function F2(f1) {
var a = 200
console.log(f1())
}
var f1 = F1()
F2(f1)
作用
- 闭包实际应用中主要用于封装变量、收敛权限。防止污染全局变量。
function isFirstLoad() {
var _list = []
return function (id) {
if (_list.indexOf(id) >= 0) {
return false
} else {
_list.push(id)
return true
}
}
}
// 使用
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
- 模拟块级作用域。
- 访问函数定义时所在的作用域。
缺点
- 闭包会导致原有的作用域链不释放,造成内存泄漏。 内存泄漏理解:其实是一个反向理解的概念,比方说内存是一捧沙子,闭包会将一些变量一直占用而不得销毁,由此可理解为 内存少了一部分,用不了,泄漏的多,就剩的少。
作用域
[[scope]]
:每个 JavaScript 函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些不可以的属性仅供 JavaScript引擎存取,[[scope]]
就是其中一个。它就是我们所说的作用域,其中存储了执行期上下文集合。
// 执行一个函数,先生成一个执行期上下文,挂在自己(作用域链)的最顶端。
function test(){}
test.[[scope]]
// scope 就是 test 的一个属性,隐式的,表示作用域。
作用域链
[[scope]]
中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做 作用域链。
通俗点说:就是当前作用域中没有寻找的变量,则向父级作用域寻找,没有的话一层一层向上直到全局作用域。这种一层一层的关系,就是作用域链
function a () {
function b () {}
b()
}
a()
a defined a.[[scope]] --> 0: GO
a doing a.[[scope]] --> 0: aAO
1: GO
b defined b.[[scope]] --> 0: aAO
1: GO
b doing b.[[scope]] --> 0: bAO
1: aAO
2: GO
自由变量
当前作用域没有声明的变量。
var a = 100
function fn() {
var b = 200
console.log(a) // 自由变量
console.log(b)
}
fn()
this
this
的值是在执行的时候才能确认,定义的时候不能确认!
var a = {
name: 'A',
fn: function () {
console.log(this.name)
}
}
a.fn() // this === a
a.fn.call({name: 'B'}) // this === {name: 'B'}
var fn1 = a.fn
fn1() // this === window
this
执行会有不同,主要集中在这几个场景中
- 作为构造函数执行。
- 作为对象属性执行。
- 作为普通函数执行。
- 用于
call
apply
bind
。
function fn1 (name, age) {
console.log(name);
console.log(this);
}
fn1.call({x: 100}, 'fj', 22); // fj {x: 100}
fn1.apply({y: 200}, ['yjx', 22]); // yjx {y: 200}
var fn2 = function (name, age) {
console.log(name);
console.log(this);
}.bind({z: 300})
fn2('gx',24); // gx {z: 300}
原型和原型链
定义
原型:是对象,它提供了一套属性和方法可供后代继承。 原型是 function 对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。
- 利用原型的特点和概念,可以提取共有属性。
- 对象如何查看原型 :隐式属性
_proto
_。 - 对象如何查看对象的构造函数:
constructor
。
原型规则
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了
null
以外)
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;
- 所有的引用类型(数组、对象、函数),都有一个
__proto__
属性(隐式原型),属性值是一个普通的对象。
obj.__proto__
arr.__proto__
fn.__proto__
- 所有的函数,都有一个
prototype
属性(显式原型),属性值也是一个普通的对象。
fn.prototype
- 所有的引用类型(数组、对象、函数),
__proto__
属性值指向它的构造函数的prototype
属性值。
obj.__proto__ === Object.prototype
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的
__proto__
(即它的构造函数的prototype
)中寻找
构造函数
构造函数内部原理:三段论
- 在函数体最前面隐式的加上 this = {};
- 执行 this.xxx = xxx;
- 隐式的返回 this;
function Foo(name, age) {
// var this = {}
this.name = name
this.age = age
this.class = 'class-1'
// return this // 默认有这一行
}
var f = new Foo('zhangsan', 20)
// var f1 = new Foo('lisi', 22) // 创建多个对象
// Foo 是 f 的 构造函数
// f 是 Foo 的 实例
var a = {}
其实是var a = new Object()
的语法糖。var a = []
其实是var a = new Array()
的语法糖。function Foo(){...}
其实是var Foo = new Function(...)
的语法糖。
原型链
// 构造函数
function Foo(name, age) {
this.name = name
}
Foo.prototype.alertName = function () {
alert(this.name)
}
// 创建示例
var f = new Foo('zhangsan')
f.printName = function () {
console.log(this.name)
}
f.printName() // 自身寻找
f.alertName() // 原型寻找
f.toString() // 原型链上寻找 f.__proto__.__proto__
写一个原型链继承的例子
- 案例一
// 动物
function Animal() {
this.eat = function () {}
}
// 狗
function Dog() {
this.bark = function () {}
}
Dog.prototype = new Animal()
// 哈士奇
var hashiqi = new Dog()
- 案例二
function Elem (id) {
this.elem = document.getElementById(id)
}
Elem.prototype.html = function (val) {
let elem = this.elem;
if (val) {
elem.innerHTML = val
return this // 链式操作
} else {
return elem.innerHTML
}
}
Elem.prototype.on = function(type,fn) {
let elem = this.elem;
elem.addEventListener(type,fn)
}
let div1 = new Elem('container')
div1.html('点击').on('click',function() {
alert('点击了')
})
异步单线程
同步和异步的区别是什么?
- 同步会阻塞代码执行,而异步不会。(比如
alert
是同步,setTimeout
是异步)。
// 以下代码执行后,打印出来的结果是什么
console.log(1)
setTimeout(function () {
console.log(2)
}, 0)
console.log(3)
setTimeout(function () {
console.log(4)
}, 1000)
console.log(5)
答案:1、3、5、2、4
因为:js 是单线程语言。当遇到异步代码时,会将其暂存在一边,不能阻塞正常代码执行。等正常代码走完,再根据异步代码执行顺序依次执行。
前端使用异步的场景有哪些?
- 定时任务:
setTimeout
,setInterval
。 - 网络请求:
ajax
请求,动态<img>
加载。 - 事件绑定。事件绑定原理也是将代码暂存,等用户操作后再执行暂存的代码。
通用事件绑定
// 源代码
var btn = document.getElementById('btn');
btn.addEventListener('click', function() {
// to do
})
// 优化后
function bindEvent (elem, type , fn) {
elem.addEventListener(type, fn)
}
bindEvent(btn, 'click', function(e) {
e.preventDefault();
// to do
})
存储
- 本地存储:
Web Storage
- 本地存储:
IndexedDB
存储需求
- 移动端网络环境因素:
- 数据响应慢,体验下降。
- 节省流量 = 节省金钱。
- 追求原生体验:
- 离线使用应用。
- 存储应用相关资源,数据。
cookie 劣势
- 存储大小限制,仅4kb左右。
- 单个域名下的数量限制,50个左右。
- 污染请求头,浪费流量。
localStorage 和 sessionStorage
- 方法(两者方法相同):
localStorage.setItem(key,value)
:设置存储内容。localStorage.getItem(key)
:获取存储内容。localStorage.removeItem(key)
:删除存储内容。localstorage.clear()
:清除所有内容。localstorage.length
:获取存储内容的个数。
- 区别
-
不同的存储时效: localstorage:存储会持久化没储存时间限制。
sessionStorage:网页会话结束时失效(关闭网页会失效,刷新没问题。大小没限制) -
不同的存储容量: localStorage容量一般在2-5Mb左右
sessionStorage存储容量不一,部分浏览器不设限
注意事项
- 存储容量超出限制
- 抛出QutotaExceededError异常(存储值时应使用try catch避免异常未捕获)
- 存储类型的限制
- 只能存储字符串。
- 注意类型转换
- sessionStorage 失效机制
- 刷新页面并不能使sessionStorage失效
- 相同url不同标签页不能共享sessionStorage数据(只能在自己的标签页:唯一性)
优化
性能与存储容量大小无关,与读取次数有关。
- 减少读取item次数。
- 单个item中尽可能多的存储数据。
indexedDB
HTML 的数据库
创建数据库
// name 数据库名字,verson 版本号
indexedDB.open(name, verson)
- 如果有这个数据库就打开,没有就创建。
- 版本号只能递增,否则会走 onerror。
let request = indexedDB.open('testDB', 3);
request.onsuccess = () => {
console.log('创建数据库成功')
}
// 当版本号降低时
request.onerror = () => {
console.log('读取数据库失败')
}
// 版本升级时触发
request.onupgradeneeded = () =>{
console.log('版本号升级成功了')
}
创建表
// 创建一个命为 test1 的表
let request = indexedDB.open('testDB', 8);
let db = request.result;
db.createObjectStore('test1')
设置主键
- 设置自增主键:{autoIncrement: true}
// 1 2 3...递增
db.createObjectStore('test1', {autoIncrement: true})
- 取数据中字段作为主键:keyPath: 字段名
db.createObjectStore('test1', {keyPath: 'id'})
- 添加内容
let json = {
id: '001',
name: 'xx',
age: 24
}
// db.createObjectStore('test1', {autoIncrement: true})
db.createObjectStore('test2', {keyPath: 'id'})
setTimeout(()=>{
let db = request.result;
// 读取表,允许读和写 readwrite 读和写,readonly 只读
let transaction = db.transaction('test2', 'readwrite')
// let transaction = db.transaction(['test1','test2'], 'readwrite')
// 读取哪个表
let store = transaction.objectStore('test2');
store.add(json)
}, 100)
表的增删改查
- 添加数据:
IDBObjectStore.add()
- 读取数据:
IDBObjectStore.get()
// 根据 key 值寻找
let requestNode = store.get('002');
// 读取所有数据
let requestNode = store.getAll();
// 执行完成后的回调,同样适用于修改、删除
requestNode.onsuccess = () => {
console.log(requestNode.result)
}
- 修改数据:
IDBObjectStore.put()
let obj = {
id: '002',
name: 'yy',
age: 27
}
store.put(obj)
- 删除数据:
IDBObjectStore.delete()
store.delete(key)
// 删除所有
store.clear()
索引
比 get 方式更方便,精准。
- 创建索引:
IDBObjectStore.createIndex
- indexName: 索引名称
- keyPath:索引字段,可以为空或者数组
- optionParameters:索引配置参数
let store1 = db.createObjectStore('test2', {
keyPath: 'id'
})
// 表名称,key值,unique 唯一性
store1.createIndex('test2', 'name', {
unique: true
})
- 查看索引
// 读取哪个表
let store = transaction.objectStore('test2');
let index = store.index('test2');
index.get('xxx').onsuccess = (e) => {
console.log(e.target.result)
}
- 好处
- 可以使用存储记录中的值进行检索
- 索引自动更新
- 索引数据自动排序
游标
- 创建游标
IDBObjectStore/IDBIndex.openCursor
-
range: 指定游标范围 | Range | Code | | --- | --- | | All keys <= x | upperBound(x) | | All keys < x | upperBound(x, true) | | All keys >= y | lowerBound(y) | | All keys > y | lowerBound(y, true) | | The keys = z | only(z) | | All keys >=x && <= y | bound(x, y) | | All keys >x && < y | bound(x, y, true, true) | | All keys >x && <= y | bound(x, y, true, false) | | All keys >=x && < y | bound(x, y, false, true) |
-
direction: 游标方向
next
: 顺序查询、prev
: 逆序查询、nextunique
: 顺序唯一查询、prevunique
: 逆序唯一查询
let requestNode = store.openCursor(IDBKeyRange.only('001'));
requestNode.onsuccess = () => {
console.log(requestNode.result.value)
}
// let requestNode = store.openCursor(IDBKeyRange.bound(100, 101, true, false));
let requestNode = store.openCursor(IDBKeyRange.upperBound(101),'prev');
requestNode.onsuccess = () => {
let cursor = requestNode.result;
if (cursor) {
console.log(cursor.value)
cursor.continue();
}
}
- 好处
- 可以查询指定数据集范围
- 拥有逆序遍历能力
优劣
- 好处
- 存储类型更加丰富
- 可以在Worker中使用
- 条件搜索优势明显
- 存储容量更大
- 劣处
- 学习曲线陡峭
- 兼容问题较多
- 跨域存储限制
Ajax
XMLHttpReques
var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', true) // 允许异步
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
// to do
}
}
}
xhr.send(null)
send(null):规定send方法是将请求发送到服务器, 但是里面的参数只在post请求下有效;而get方法下直接忽略send里面的值,默认为null。
- readyState(4 最常用)
- 0 - (未初始化) 还没有调用send()方法
- 1 - (载入) 已调用send(方法,正在发送请求
- 2 - (载入完成) send()方法执行完成,已经接收到全部响应内容
- 3 - (交互) 正在解析响应内容
- 4 - (完成) 响应内容解析完成,可以在客户端调用了
- status(200 最常用)
- 2xx - 表示成功处理请求。如200。
- 3xx - 需要重定向,浏览器直接跳转。
- 4xx - 客户端请求错误,如404。
- 5xx - 服务器端错误。
JQuery 中 ajax的调用
$.ajax({
type: 'POST', // 请求方式
async: false, // 同步
url: '...', // 请求地址
dataType: 'json',
contentType: 'application/json',
data: { // 入参
name: 'fj',password: 'xxx'
},
beforeSend(request) { // 设置头部header入参 非必须
request.setRequestHeader('appId','xxx')
request.setRequestHeader('token','xxx')
},
success(res) {
// to do
},
error(err) {
// to do
}
})
跨域
- 什么是跨域?
- 浏览器有同源策略,不允许ajax访问其他域接口。
- 跨域条件:协议、域名、端口,有一个不同就算跨域。
- 如何解决跨域?
- 针对前端,跨域的实现方式就是JSONP。
- 针对后端,跨域的实现方式就是设置 http header。
- 跨域注意事项。
- 所有的跨域请求都必须经过信息提供方允许。
- 如果未经允许即可获取,那是浏览器同源策略出现漏洞。
- 可以跨域的三个标签。
<img src=xxx>
<link href=xxxx>
<script src=xxx>
- 三个标签的场景。
<img>
用于打点统计,统计网站可能是其他域(img没有跨域限制,可以兼容各种浏览器)。<link>
<script>
可以使用CDN,CDN的也是其他域。<script>
可以用于JSONP。
JSONP
- 原理 script 标签可以逃避浏览器的同源策略。首先定义一个函数,然后请求外域的一个API,API返回的是一个JS片段,JS片段正好执行这个函数,然后就把参数传过来,如此就可请求到跨域数据。
<script>
window.callback = function(data) {
// 跨域得到的信息
console.log(data)
}
</script>
<script src="http://www.xxx.com/api.js"></script>
// 以上将返回 callback({x: 100, y: 200})
- JQuery 中 JSONP
$.ajax({
url: 'http://www.xxx.com/xx.json', // 跨域接口
dataType: 'jsonp',
jsonpCallback: 'myCallback', // 商议好的内容
success: function (result) {
console.log(result)
}
})
// myCallback ({x: 100, y: 200})
// result ({x: 100, y: 200})